From 561094f58559d1bd349d8b8ee2f2d1f84347de5f Mon Sep 17 00:00:00 2001 From: Boris Bera Date: Sat, 4 Nov 2023 15:17:55 -0400 Subject: [PATCH 1/7] Ensure that options are always passed down Signed-off-by: Boris Bera --- .../StructuredMetadataTable.tsx | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx index 2b18f6ad19..108ddbe926 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx @@ -65,9 +65,11 @@ const StyledNestedList = withStyles(nestedListStyle, { {children} )); -function renderList(list: Array, nested?: boolean) { +function renderList(list: Array, options: Options, nested: boolean) { const values = list.map((item: any, index: number) => ( - {toValue(item)} + + {toValue(item, options, nested)} + )); return nested ? ( {values} @@ -78,15 +80,12 @@ function renderList(list: Array, nested?: boolean) { function renderMap( map: { [key: string]: any }, - nested?: boolean, - options?: any, + options: Options, + nested: boolean, ) { const values = Object.keys(map).map(key => { - const value = toValue(map[key], true); - const fmtKey = - options && options.titleFormat - ? options.titleFormat(key) - : startCase(key); + const value = toValue(map[key], options, true); + const fmtKey = options.titleFormat?.(key) ?? startCase(key); return ( @@ -106,8 +105,8 @@ function renderMap( function toValue( value: ReactElement | object | Array | boolean, - options?: any, - nested?: boolean, + options: Options, + nested: boolean, ) { if (React.isValidElement(value)) { return {value}; @@ -118,7 +117,7 @@ function toValue( } if (Array.isArray(value)) { - return renderList(value, nested); + return renderList(value, options, nested); } if (typeof value === 'boolean') { @@ -131,8 +130,8 @@ function toValue( ); } -const ItemValue = ({ value, options }: { value: any; options: any }) => ( - {toValue(value, options)} +const ItemValue = ({ value, options }: { value: any; options: Options }) => ( + {toValue(value, options, false)} ); const TableItem = ({ @@ -142,31 +141,29 @@ const TableItem = ({ }: { title: string; value: any; - options: any; + options: Options; }) => { return ( - + ); }; -function mapToItems(info: { [key: string]: string }, options: any) { +function mapToItems(info: { [key: string]: string }, options: Options) { return Object.keys(info).map(key => ( )); } +interface Options { + titleFormat?: (key: string) => string; +} + type Props = { metadata: { [key: string]: any }; dense?: boolean; - options?: any; + options?: Options; }; export function StructuredMetadataTable(props: Props) { From b39c242667a2451e6ceb2faad3b7e1ee204779f1 Mon Sep 17 00:00:00 2001 From: Boris Bera Date: Sun, 5 Nov 2023 06:19:07 -0500 Subject: [PATCH 2/7] Document `options.titleFormat` Signed-off-by: Boris Bera --- .../StructuredMetadataTable/StructuredMetadataTable.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx index 108ddbe926..08c00da772 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx @@ -157,6 +157,13 @@ function mapToItems(info: { [key: string]: string }, options: Options) { } interface Options { + /** + * Function to format the keys from the `metadata` object. Defaults to + * startCase from the lodash library. + * @param key A key within the `metadata` + * @returns Formatted key + * @see {@link startCase} + */ titleFormat?: (key: string) => string; } From ce8a6bd6f8ff83ec70e7831ca55798b178050c53 Mon Sep 17 00:00:00 2001 From: Boris Bera Date: Sun, 5 Nov 2023 06:41:41 -0500 Subject: [PATCH 3/7] Fix the exported documentation for `StructuredMetadataTable` Signed-off-by: Boris Bera --- packages/core-components/api-report.md | 49 ++++++++++++------- .../StructuredMetadataTable.tsx | 43 ++++++++-------- .../StructuredMetadataTable/index.tsx | 5 +- 3 files changed, 58 insertions(+), 39 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 495150e54b..7df74ee892 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -120,10 +120,10 @@ export type BottomLinkProps = { onClick?: (event: React_2.MouseEvent) => void; }; -// Warning: (ae-forgotten-export) The symbol "Props_19" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "Props_18" needs to be exported by the entry point index.d.ts // // @public -export function Breadcrumbs(props: Props_19): React_2.JSX.Element; +export function Breadcrumbs(props: Props_18): React_2.JSX.Element; // @public (undocumented) export type BreadcrumbsClickableTextClassKey = 'root'; @@ -177,11 +177,11 @@ export interface CodeSnippetProps { text: string; } -// Warning: (ae-forgotten-export) The symbol "Props_13" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "Props_12" needs to be exported by the entry point index.d.ts // // @public export function Content( - props: PropsWithChildren, + props: PropsWithChildren, ): React_2.JSX.Element; // Warning: (ae-forgotten-export) The symbol "ContentHeaderProps" needs to be exported by the entry point index.d.ts @@ -454,10 +454,10 @@ export function GitHubIcon(props: IconComponentProps): React_2.JSX.Element; // @public (undocumented) export function GroupIcon(props: IconComponentProps): React_2.JSX.Element; -// Warning: (ae-forgotten-export) The symbol "Props_14" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "Props_13" needs to be exported by the entry point index.d.ts // // @public -export function Header(props: PropsWithChildren): React_2.JSX.Element; +export function Header(props: PropsWithChildren): React_2.JSX.Element; // @public (undocumented) export function HeaderActionMenu( @@ -564,10 +564,10 @@ export type IconLinkVerticalProps = { // @public (undocumented) export type IdentityProviders = ('guest' | 'custom' | SignInProviderConfig)[]; -// Warning: (ae-forgotten-export) The symbol "Props_15" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "Props_14" needs to be exported by the entry point index.d.ts // // @public -export function InfoCard(props: Props_15): JSX.Element; +export function InfoCard(props: Props_14): JSX.Element; // @public (undocumented) export type InfoCardClassKey = @@ -795,11 +795,11 @@ export function OverflowTooltip(props: Props_9): React_2.JSX.Element; // @public (undocumented) export type OverflowTooltipClassKey = 'container'; -// Warning: (ae-forgotten-export) The symbol "Props_16" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "Props_15" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "Page" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function Page(props: Props_16): React_2.JSX.Element; +export function Page(props: Props_15): React_2.JSX.Element; // Warning: (ae-missing-release-tag) "PageClassKey" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1125,11 +1125,11 @@ export type SidebarSubmenuProps = { children: ReactNode; }; -// Warning: (ae-forgotten-export) The symbol "Props_17" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "Props_16" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SignInPage" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function SignInPage(props: Props_17): React_2.JSX.Element; +export function SignInPage(props: Props_16): React_2.JSX.Element; // Warning: (ae-missing-release-tag) "SignInPageClassKey" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1222,11 +1222,10 @@ export function StatusWarning( props: PropsWithChildren<{}>, ): React_2.JSX.Element; -// Warning: (ae-forgotten-export) The symbol "Props_12" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "StructuredMetadataTable" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) -export function StructuredMetadataTable(props: Props_12): React_2.JSX.Element; +export function StructuredMetadataTable( + props: StructuredMetadataTableProps, +): React_2.JSX.Element; // Warning: (ae-missing-release-tag) "StructuredMetadataTableListClassKey" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1238,6 +1237,20 @@ export type StructuredMetadataTableListClassKey = 'root'; // @public (undocumented) export type StructuredMetadataTableNestedListClassKey = 'root'; +// @public (undocumented) +export interface StructuredMetadataTableProps { + // (undocumented) + dense?: boolean; + // (undocumented) + metadata: { + [key: string]: any; + }; + // (undocumented) + options?: { + titleFormat?: (key: string) => string; + }; +} + // @public (undocumented) export type SubmenuOptions = { drawerWidthClosed?: number; @@ -1305,12 +1318,12 @@ export type Tab = { >; }; -// Warning: (ae-forgotten-export) The symbol "Props_18" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "Props_17" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "TabbedCard" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export function TabbedCard( - props: PropsWithChildren, + props: PropsWithChildren, ): React_2.JSX.Element; // Warning: (ae-missing-release-tag) "TabbedCardClassKey" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx index 08c00da772..3099edf9d8 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx @@ -85,11 +85,10 @@ function renderMap( ) { const values = Object.keys(map).map(key => { const value = toValue(map[key], options, true); - const fmtKey = options.titleFormat?.(key) ?? startCase(key); return ( - {`${fmtKey}: `} + {`${options.titleFormat(key)}: `} {value} @@ -144,7 +143,7 @@ const TableItem = ({ options: Options; }) => { return ( - + ); @@ -156,25 +155,29 @@ function mapToItems(info: { [key: string]: string }, options: Options) { )); } -interface Options { - /** - * Function to format the keys from the `metadata` object. Defaults to - * startCase from the lodash library. - * @param key A key within the `metadata` - * @returns Formatted key - * @see {@link startCase} - */ - titleFormat?: (key: string) => string; -} - -type Props = { +/** @public */ +export interface StructuredMetadataTableProps { metadata: { [key: string]: any }; dense?: boolean; - options?: Options; -}; + options?: { + /** + * Function to format the keys from the `metadata` object. Defaults to + * startCase from the lodash library. + * @param key - A key within the `metadata` + * @returns Formatted key + */ + titleFormat?: (key: string) => string; + }; +} -export function StructuredMetadataTable(props: Props) { - const { metadata, dense = true, options } = props; - const metadataItems = mapToItems(metadata, options || {}); +type Options = Required>; + +/** @public */ +export function StructuredMetadataTable(props: StructuredMetadataTableProps) { + const { metadata, dense = true, options = {} } = props; + const metadataItems = mapToItems(metadata, { + titleFormat: startCase, + ...options, + }); return {metadataItems}; } diff --git a/packages/core-components/src/components/StructuredMetadataTable/index.tsx b/packages/core-components/src/components/StructuredMetadataTable/index.tsx index 855df31e2f..e45b4a862f 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/index.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/index.tsx @@ -20,7 +20,10 @@ export type { MetadataTableListClassKey, MetadataTableListItemClassKey, } from './MetadataTable'; -export { StructuredMetadataTable } from './StructuredMetadataTable'; +export { + StructuredMetadataTable, + type StructuredMetadataTableProps, +} from './StructuredMetadataTable'; export type { StructuredMetadataTableListClassKey, StructuredMetadataTableNestedListClassKey, From f4056eb937b51895617f8fcc6ab084f89f39efd2 Mon Sep 17 00:00:00 2001 From: Boris Bera Date: Sun, 5 Nov 2023 07:13:35 -0500 Subject: [PATCH 4/7] Test nested key formatting Signed-off-by: Boris Bera --- .../StructuredMetadataTable.test.tsx | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.test.tsx b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.test.tsx index db7c53897d..4eb05e715e 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.test.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.test.tsx @@ -115,4 +115,60 @@ describe('', () => { } }); }); + + describe('Title formatting', () => { + const metadata = { + testA: 'stuff', + testB: { testC: 'stuff' }, + testD: [{ testE: 'stuff' }], + }; + + it('should make keys human readable', async () => { + const rendered = render(); + expect(rendered.queryByText(/^Test A/)).toBeInTheDocument(); + expect(rendered.queryByText(/^Test B/)).toBeInTheDocument(); + expect(rendered.queryByText(/^Test C/)).toBeInTheDocument(); + expect(rendered.queryByText(/^Test D/)).toBeInTheDocument(); + expect(rendered.queryByText(/^Test E/)).toBeInTheDocument(); + }); + + it('should be possible to disable it', async () => { + const rendered = render( + key }} + />, + ); + expect(rendered.queryByText(/^testA/)).toBeInTheDocument(); + expect(rendered.queryByText(/^testB/)).toBeInTheDocument(); + expect(rendered.queryByText(/^testC/)).toBeInTheDocument(); + expect(rendered.queryByText(/^testD/)).toBeInTheDocument(); + expect(rendered.queryByText(/^testE/)).toBeInTheDocument(); + }); + + it('should be customizable', async () => { + const spongeBobCase = (key: string) => + key + .split('') + .map((letter, index) => { + if (index % 2 === 0) { + return letter.toLocaleLowerCase('en-US'); + } + return letter.toLocaleUpperCase('en-US'); + }) + .join(''); + + const rendered = render( + , + ); + expect(rendered.queryByText(/^tEsTa/)).toBeInTheDocument(); + expect(rendered.queryByText(/^tEsTb/)).toBeInTheDocument(); + expect(rendered.queryByText(/^tEsTc/)).toBeInTheDocument(); + expect(rendered.queryByText(/^tEsTd/)).toBeInTheDocument(); + expect(rendered.queryByText(/^tEsTe/)).toBeInTheDocument(); + }); + }); }); From 66071b42c03d988665c6d75d08a92d162f2de92f Mon Sep 17 00:00:00 2001 From: Boris Bera Date: Sun, 5 Nov 2023 07:41:05 -0500 Subject: [PATCH 5/7] Add changeset for `StructuredMetadataTable` improvements Signed-off-by: Boris Bera --- .changeset/poor-roses-hang.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/poor-roses-hang.md diff --git a/.changeset/poor-roses-hang.md b/.changeset/poor-roses-hang.md new file mode 100644 index 0000000000..bc32b76f6f --- /dev/null +++ b/.changeset/poor-roses-hang.md @@ -0,0 +1,7 @@ +--- +'@backstage/core-components': patch +--- + +Make the `options.titleFormat` prop of `` apply to all keys including nested ones. Previously, this option would only apply to the root keys of the `metadata` prop. + +Document and improve the type of the `options.titleFormat` prop of ``. Previously, the `options` prop was `any`. From 35be92c53e1578898d4c4b0fc80d8d450865b791 Mon Sep 17 00:00:00 2001 From: Boris Bera Date: Sun, 5 Nov 2023 07:59:29 -0500 Subject: [PATCH 6/7] Add storybook story for `StructuredMetadataTable` without key formatting Signed-off-by: Boris Bera --- .../StructuredMetadataTable.stories.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.stories.tsx b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.stories.tsx index f48e959073..17d01090a7 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.stories.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.stories.tsx @@ -71,3 +71,19 @@ export const NotDenseTable = () => ( ); + +export const WithoutKeyFormatting = () => ( + + +
+ key }} + /> +
+
+
+); From 69ae9834701127cd67b24889f8f04998c60b6a17 Mon Sep 17 00:00:00 2001 From: Boris Bera Date: Tue, 7 Nov 2023 13:50:26 -0500 Subject: [PATCH 7/7] Update .changeset/poor-roses-hang.md Co-authored-by: Vincenzo Scamporlino Signed-off-by: Boris Bera --- .changeset/poor-roses-hang.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/poor-roses-hang.md b/.changeset/poor-roses-hang.md index bc32b76f6f..028df6a7c6 100644 --- a/.changeset/poor-roses-hang.md +++ b/.changeset/poor-roses-hang.md @@ -2,6 +2,6 @@ '@backstage/core-components': patch --- -Make the `options.titleFormat` prop of `` apply to all keys including nested ones. Previously, this option would only apply to the root keys of the `metadata` prop. +Make the `options.titleFormat` prop of `` apply to all keys including nested ones. Previously, this option would only apply to the root keys of the `metadata` prop. -Document and improve the type of the `options.titleFormat` prop of ``. Previously, the `options` prop was `any`. +Document and improve the props of ``. Previously, the `options` prop was `any`.