diff --git a/web-console/src/components/plural-pair-if-needed/__snapshots__/plural-pair-if-needed.spec.tsx.snap b/web-console/src/components/plural-pair-if-needed/__snapshots__/plural-pair-if-needed.spec.tsx.snap new file mode 100644 index 00000000000..1258b5d8438 --- /dev/null +++ b/web-console/src/components/plural-pair-if-needed/__snapshots__/plural-pair-if-needed.spec.tsx.snap @@ -0,0 +1,27 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`plural pair if needed works when both counts exist 1`] = ` +

+ 2 running tasks, 1 pending task +

+`; + +exports[`plural pair if needed works when no counts exist 1`] = `null`; + +exports[`plural pair if needed works when only first count exists 1`] = ` +

+ 2 running tasks +

+`; + +exports[`plural pair if needed works when only second count exists 1`] = ` +

+ 1 pending task +

+`; diff --git a/web-console/src/components/plural-pair-if-needed/plural-pair-if-needed.spec.tsx b/web-console/src/components/plural-pair-if-needed/plural-pair-if-needed.spec.tsx new file mode 100644 index 00000000000..6087c7e7b92 --- /dev/null +++ b/web-console/src/components/plural-pair-if-needed/plural-pair-if-needed.spec.tsx @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { render } from '@testing-library/react'; +import React from 'react'; + +import { PluralPairIfNeeded } from './plural-pair-if-needed'; + +describe('plural pair if needed', () => { + it('works when both counts exist', () => { + const pluralPairIfNeeded = ( + + ); + + const { container } = render(pluralPairIfNeeded); + expect(container.firstChild).toMatchSnapshot(); + }); + + it('works when only first count exists', () => { + const pluralPairIfNeeded = ( + + ); + + const { container } = render(pluralPairIfNeeded); + expect(container.firstChild).toMatchSnapshot(); + }); + + it('works when only second count exists', () => { + const pluralPairIfNeeded = ( + + ); + + const { container } = render(pluralPairIfNeeded); + expect(container.firstChild).toMatchSnapshot(); + }); + + it('works when no counts exist', () => { + const pluralPairIfNeeded = ( + + ); + + const { container } = render(pluralPairIfNeeded); + expect(container.firstChild).toMatchSnapshot(); + }); +}); diff --git a/web-console/src/components/plural-pair-if-needed/plural-pair-if-needed.tsx b/web-console/src/components/plural-pair-if-needed/plural-pair-if-needed.tsx new file mode 100644 index 00000000000..c26c6335545 --- /dev/null +++ b/web-console/src/components/plural-pair-if-needed/plural-pair-if-needed.tsx @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; + +import { compact, pluralIfNeeded } from '../../utils'; + +export interface PluralPairIfNeededProps { + firstCount: number; + firstSingular: string; + secondCount: number; + secondSingular: string; +} + +export const PluralPairIfNeeded = React.memo(function PluralPairIfNeeded( + props: PluralPairIfNeededProps, +) { + const { firstCount, firstSingular, secondCount, secondSingular } = props; + + const text = compact([ + firstCount ? pluralIfNeeded(firstCount, firstSingular) : undefined, + secondCount ? pluralIfNeeded(secondCount, secondSingular) : undefined, + ]).join(', '); + if (!text) return null; + return

{text}

; +}); diff --git a/web-console/src/views/home-view/datasources-card/datasources-card.tsx b/web-console/src/views/home-view/datasources-card/datasources-card.tsx index bed26d7a76b..f36bd18c516 100644 --- a/web-console/src/views/home-view/datasources-card/datasources-card.tsx +++ b/web-console/src/views/home-view/datasources-card/datasources-card.tsx @@ -94,7 +94,7 @@ export class DatasourcesCard extends React.PureComponent< loading={datasourceCountLoading} error={datasourceCountError} > - {pluralIfNeeded(datasourceCount, 'datasource')} +

{pluralIfNeeded(datasourceCount, 'datasource')}

); } diff --git a/web-console/src/views/home-view/home-view-card/home-view-card.scss b/web-console/src/views/home-view/home-view-card/home-view-card.scss index 82b4b9393cf..3c63840af55 100644 --- a/web-console/src/views/home-view/home-view-card/home-view-card.scss +++ b/web-console/src/views/home-view/home-view-card/home-view-card.scss @@ -20,4 +20,17 @@ .bp3-card { height: 170px; } + + &:hover { + text-decoration: none; + } + + p { + padding-left: 20px; + } +} + +.bp3-dark a.home-view-card, +.bp3-dark a.home-view-card:hover { + color: inherit; } diff --git a/web-console/src/views/home-view/home-view.scss b/web-console/src/views/home-view/home-view.scss index e5687504741..9f892c4fb10 100644 --- a/web-console/src/views/home-view/home-view.scss +++ b/web-console/src/views/home-view/home-view.scss @@ -22,9 +22,5 @@ display: grid; gap: $standard-padding; grid-template-columns: 1fr 1fr 1fr 1fr; - - & > a { - text-decoration: inherit; - color: inherit; - } + padding: 0 60px; } diff --git a/web-console/src/views/home-view/services-card/services-card.tsx b/web-console/src/views/home-view/services-card/services-card.tsx index ec39585d477..09d5cf08cd3 100644 --- a/web-console/src/views/home-view/services-card/services-card.tsx +++ b/web-console/src/views/home-view/services-card/services-card.tsx @@ -20,7 +20,8 @@ import { IconNames } from '@blueprintjs/icons'; import axios from 'axios'; import React from 'react'; -import { compact, lookupBy, pluralIfNeeded, queryDruidSql, QueryManager } from '../../../utils'; +import { PluralPairIfNeeded } from '../../../components/plural-pair-if-needed/plural-pair-if-needed'; +import { lookupBy, queryDruidSql, QueryManager } from '../../../utils'; import { Capabilities } from '../../../utils/capabilities'; import { HomeViewCard } from '../home-view-card/home-view-card'; @@ -42,20 +43,6 @@ export interface ServicesCardState { } export class ServicesCard extends React.PureComponent { - static renderPluralIfNeededPair( - count1: number, - singular1: string, - count2: number, - singular2: string, - ): JSX.Element | undefined { - const text = compact([ - count1 ? pluralIfNeeded(count1, singular1) : undefined, - count2 ? pluralIfNeeded(count2, singular2) : undefined, - ]).join(', '); - if (!text) return; - return

{text}

; - } - private serviceQueryManager: QueryManager; constructor(props: ServicesCardProps, context: any) { @@ -147,20 +134,30 @@ export class ServicesCard extends React.PureComponent - {ServicesCard.renderPluralIfNeededPair( - overlordCount, - 'overlord', - coordinatorCount, - 'coordinator', - )} - {ServicesCard.renderPluralIfNeededPair(routerCount, 'router', brokerCount, 'broker')} - {ServicesCard.renderPluralIfNeededPair( - historicalCount, - 'historical', - middleManagerCount, - 'middle manager', - )} - {ServicesCard.renderPluralIfNeededPair(peonCount, 'peon', indexerCount, 'indexer')} + + + + ); } diff --git a/web-console/src/views/home-view/tasks-card/tasks-card.tsx b/web-console/src/views/home-view/tasks-card/tasks-card.tsx index d8e2a49dea3..b0efe6a001b 100644 --- a/web-console/src/views/home-view/tasks-card/tasks-card.tsx +++ b/web-console/src/views/home-view/tasks-card/tasks-card.tsx @@ -20,6 +20,7 @@ import { IconNames } from '@blueprintjs/icons'; import axios from 'axios'; import React from 'react'; +import { PluralPairIfNeeded } from '../../../components/plural-pair-if-needed/plural-pair-if-needed'; import { lookupBy, pluralIfNeeded, queryDruidSql, QueryManager } from '../../../utils'; import { Capabilities } from '../../../utils/capabilities'; import { HomeViewCard } from '../home-view-card/home-view-card'; @@ -124,8 +125,12 @@ GROUP BY 1`, loading={taskCountLoading} error={taskCountError} > - {Boolean(runningTaskCount) &&

{pluralIfNeeded(runningTaskCount, 'running task')}

} - {Boolean(pendingTaskCount) &&

{pluralIfNeeded(pendingTaskCount, 'pending task')}

} + {Boolean(successTaskCount) &&

{pluralIfNeeded(successTaskCount, 'successful task')}

} {Boolean(waitingTaskCount) &&

{pluralIfNeeded(waitingTaskCount, 'waiting task')}

} {Boolean(failedTaskCount) &&

{pluralIfNeeded(failedTaskCount, 'failed task')}

}