From 5207a2a24f7d383057b56a91add96e00e5c36d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C8=98tefan=20Ciuprina?= <57374665+stefanc18@users.noreply.github.com> Date: Sat, 4 Nov 2023 01:06:49 +0200 Subject: [PATCH] Expose width and height of nimble-dialog & add tokens (#1553) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Pull Request ## ๐Ÿคจ Rationale #1117 ## ๐Ÿ‘ฉโ€๐Ÿ’ป Implementation - Performed the tasks that are enumerated in the issue linked above. - Create 2 tokens for the existing (small) dialog: width & max-height - Create 2 tokens for the large dialog: width & height - Have the small dialog as the default (width & max-height of small dialog; height set to `fit-content`, this was the default height that was applied to the dialog until now) - Added storybook documentation. ## ๐Ÿงช Testing Manually thorugh the storybook tests & matrix tests in chromatic. The sizes are configurable in the storybook similarly to how the width is configurable for the nimble drawer. For the matrix tests, I added 2 stories: one for the small dialog and one for the large dialog: https://www.chromatic.com/build?appId=60e89457a987cf003efc0a5b&number=7776 ## โœ… Checklist - [X] I have updated the project documentation to reflect my changes or determined no changes are needed. --- ...-1e4a5579-af4c-4960-8cfc-32d8417d1a34.json | 7 +++ .../nimble-components/src/dialog/styles.ts | 10 +++- .../nimble-components/src/dialog/template.ts | 1 + .../src/dialog/tests/dialog-matrix.stories.ts | 55 ++++++++++++++++++- .../src/dialog/tests/dialog.mdx | 46 ++++++++++++++++ .../src/dialog/tests/dialog.stories.ts | 55 ++++++++++++++++++- .../src/dialog/tests/types.ts | 7 +++ .../theme-provider/design-token-comments.ts | 9 +++ .../src/theme-provider/design-token-names.ts | 7 +++ .../src/theme-provider/design-tokens.ts | 18 ++++++ .../theme-provider/tests/tokens.stories.ts | 1 + 11 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 change/@ni-nimble-components-1e4a5579-af4c-4960-8cfc-32d8417d1a34.json create mode 100644 packages/nimble-components/src/dialog/tests/dialog.mdx diff --git a/change/@ni-nimble-components-1e4a5579-af4c-4960-8cfc-32d8417d1a34.json b/change/@ni-nimble-components-1e4a5579-af4c-4960-8cfc-32d8417d1a34.json new file mode 100644 index 0000000000..f0e50e10fa --- /dev/null +++ b/change/@ni-nimble-components-1e4a5579-af4c-4960-8cfc-32d8417d1a34.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Expose width and height of nimble-dialog & add tokens", + "packageName": "@ni/nimble-components", + "email": "57374665+stefanc18@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/nimble-components/src/dialog/styles.ts b/packages/nimble-components/src/dialog/styles.ts index c3b4fab267..653fcd6c0b 100644 --- a/packages/nimble-components/src/dialog/styles.ts +++ b/packages/nimble-components/src/dialog/styles.ts @@ -12,7 +12,10 @@ import { smallPadding, subtitleFont, subtitleFontColor, - elevation3BoxShadow + elevation3BoxShadow, + dialogSmallWidth, + dialogSmallHeight, + dialogSmallMaxHeight } from '../theme-provider/design-tokens'; import { modalBackdropColorThemeColorStatic, @@ -32,8 +35,9 @@ export const styles = css` border: none; box-shadow: ${elevation3BoxShadow}; padding: 0px; - width: 400px; - max-height: 600px; + width: ${dialogSmallWidth}; + height: ${dialogSmallHeight}; + max-height: ${dialogSmallMaxHeight}; } dialog[open] { diff --git a/packages/nimble-components/src/dialog/template.ts b/packages/nimble-components/src/dialog/template.ts index e124f76214..d27899a530 100644 --- a/packages/nimble-components/src/dialog/template.ts +++ b/packages/nimble-components/src/dialog/template.ts @@ -6,6 +6,7 @@ export const template = html` diff --git a/packages/nimble-components/src/dialog/tests/dialog-matrix.stories.ts b/packages/nimble-components/src/dialog/tests/dialog-matrix.stories.ts index f5e2999179..d073fb8af8 100644 --- a/packages/nimble-components/src/dialog/tests/dialog-matrix.stories.ts +++ b/packages/nimble-components/src/dialog/tests/dialog-matrix.stories.ts @@ -1,10 +1,19 @@ import type { StoryFn, Meta } from '@storybook/html'; -import { html } from '@microsoft/fast-element'; +import { html, ViewTemplate } from '@microsoft/fast-element'; import { createFixedThemeStory } from '../../utilities/tests/storybook'; import { sharedMatrixParameters } from '../../utilities/tests/matrix'; import { backgroundStates } from '../../utilities/tests/states'; import { dialogTag } from '..'; import { buttonTag } from '../../button'; +import { + bodyFont, + dialogLargeHeight, + dialogLargeMaxHeight, + dialogLargeWidth, + dialogSmallHeight, + dialogSmallMaxHeight, + dialogSmallWidth +} from '../../theme-provider/design-tokens'; const metadata: Meta = { title: 'Tests/Dialog', @@ -15,6 +24,12 @@ const metadata: Meta = { export default metadata; +const sizeStates = [ + `width: var(${dialogSmallWidth.cssCustomProperty}); height: var(${dialogSmallHeight.cssCustomProperty}); max-height: var(${dialogSmallMaxHeight.cssCustomProperty});`, + `width: var(${dialogLargeWidth.cssCustomProperty}); height: var(${dialogLargeHeight.cssCustomProperty}); max-height: var(${dialogLargeMaxHeight.cssCustomProperty});` +] as const; +type SizeState = (typeof sizeStates)[number]; + const component = html` <${dialogTag}> This is my dialog's title. It is pretty long. @@ -28,6 +43,30 @@ const component = html` `; +const dialogSizingTestCase = (size: SizeState): ViewTemplate => html` +

${() => size};

+ + <${dialogTag}> + This is my dialog's title. It is pretty long. + The dialog has a subtitle here. +
Here is the first piece of content in the dialog
+
+ Here is another piece of content in the dialog. It is a bit longer. +
+ <${buttonTag} slot="footer">Cancel + <${buttonTag} slot="footer">OK + +`; + const [ lightThemeWhiteBackground, colorThemeDarkGreenBackground, @@ -60,3 +99,17 @@ export const dialogDarkThemeBlackBackground: StoryFn = createFixedThemeStory( ); dialogDarkThemeBlackBackground.play = playFunction; + +export const dialogSmallSize: StoryFn = createFixedThemeStory( + dialogSizingTestCase(sizeStates[0]), + lightThemeWhiteBackground +); + +dialogSmallSize.play = playFunction; + +export const dialogLargeSize: StoryFn = createFixedThemeStory( + dialogSizingTestCase(sizeStates[1]), + lightThemeWhiteBackground +); + +dialogLargeSize.play = playFunction; diff --git a/packages/nimble-components/src/dialog/tests/dialog.mdx b/packages/nimble-components/src/dialog/tests/dialog.mdx new file mode 100644 index 0000000000..5cb5ca52ab --- /dev/null +++ b/packages/nimble-components/src/dialog/tests/dialog.mdx @@ -0,0 +1,46 @@ +import { + DocsStory, + Meta, + Controls, + Stories, + Title, + Description +} from '@storybook/blocks'; +import * as dialogStories from './dialog.stories'; + + + + +<Description of={dialogStories} /> + +<DocsStory of={dialogStories.dialog} expanded={false} /> +<Controls of={dialogStories.dialog} /> + +# Usage Docs + +<br /> + +## Sizing + +The dialog size can be customized by modyfing the width, height and max-height properties of `nimble-dialog::part(control)`. + +By default, the dialog is sized to be small and growable. This should be used for small dialogs like a confirmation dialog. +This is equivalent of using the following style configuration: + +```scss +nimble-dialog::part(control) { + width: $ni-nimble-dialog-small-width; + height: $ni-nimble-dialog-small-height; + max-height: $ni-nimble-dialog-small-max-height; +} +``` + +For larger dialogs, for example a wizard-like dialog, the following style configuration should be used: + +```scss +nimble-dialog::part(control) { + width: $ni-nimble-dialog-large-width; + height: $ni-nimble-dialog-large-height; + max-height: $ni-nimble-dialog-large-max-height; +} +``` diff --git a/packages/nimble-components/src/dialog/tests/dialog.stories.ts b/packages/nimble-components/src/dialog/tests/dialog.stories.ts index 8321cc5d31..7f2d9f7a06 100644 --- a/packages/nimble-components/src/dialog/tests/dialog.stories.ts +++ b/packages/nimble-components/src/dialog/tests/dialog.stories.ts @@ -3,10 +3,18 @@ import type { Meta, StoryObj } from '@storybook/html'; import { createUserSelectedThemeStory } from '../../utilities/tests/storybook'; import { Dialog, dialogTag, UserDismissed } from '..'; import { TextField, textFieldTag } from '../../text-field'; -import { ExampleContentType } from './types'; +import { DialogSizeOptions, ExampleContentType } from './types'; import { loremIpsum } from '../../utilities/tests/lorem-ipsum'; import { buttonTag } from '../../button'; import { checkboxTag } from '../../checkbox'; +import { + dialogLargeHeight, + dialogLargeMaxHeight, + dialogLargeWidth, + dialogSmallHeight, + dialogSmallMaxHeight, + dialogSmallWidth +} from '../../theme-provider/design-tokens'; interface DialogArgs { title: string; @@ -16,6 +24,7 @@ interface DialogArgs { includeFooterButtons: boolean; preventDismiss: boolean; content: ExampleContentType; + size: DialogSizeOptions; show: undefined; close: undefined; dialogRef: Dialog<string>; @@ -49,9 +58,29 @@ const content = { [ExampleContentType.longContent]: longContent } as const; +const sizeDescription = ` +Size of a nimble dialog. + +See the Sizing section of the Usage Docs for information on controlling the size of the dialog. +`; + +const widths = { + [DialogSizeOptions.smallGrowable]: `var(${dialogSmallWidth.cssCustomProperty})`, + [DialogSizeOptions.largeFixed]: `var(${dialogLargeWidth.cssCustomProperty})` +} as const; + +const heights = { + [DialogSizeOptions.smallGrowable]: `var(${dialogSmallHeight.cssCustomProperty})`, + [DialogSizeOptions.largeFixed]: `var(${dialogLargeHeight.cssCustomProperty})` +} as const; + +const maxHeights = { + [DialogSizeOptions.smallGrowable]: `var(${dialogSmallMaxHeight.cssCustomProperty})`, + [DialogSizeOptions.largeFixed]: `var(${dialogLargeMaxHeight.cssCustomProperty})` +} as const; + const metadata: Meta<DialogArgs> = { title: 'Components/Dialog', - tags: ['autodocs'], parameters: { docs: { description: { @@ -65,6 +94,13 @@ const metadata: Meta<DialogArgs> = { .first-button { margin-right: auto; } + ${dialogTag}::part(control) { + ${x => ` + width:${widths[x.size]}; + height:${heights[x.size]}; + max-height:${maxHeights[x.size]}; + `} + } </style> <${dialogTag} ${ref('dialogRef')} @@ -154,6 +190,20 @@ const metadata: Meta<DialogArgs> = { } } }, + size: { + description: sizeDescription, + options: [ + DialogSizeOptions.smallGrowable, + DialogSizeOptions.largeFixed + ], + control: { + type: 'radio', + labels: { + [DialogSizeOptions.smallGrowable]: 'Small growable', + [DialogSizeOptions.largeFixed]: 'Large fixed' + } + } + }, show: { name: 'show()', description: @@ -178,6 +228,7 @@ const metadata: Meta<DialogArgs> = { includeFooterButtons: true, preventDismiss: false, content: ExampleContentType.shortContent, + size: DialogSizeOptions.smallGrowable, openAndHandleResult: (dialogRef, textFieldRef) => { void (async () => { const reason = await dialogRef.show(); diff --git a/packages/nimble-components/src/dialog/tests/types.ts b/packages/nimble-components/src/dialog/tests/types.ts index ae5d2ca4fb..7c5787c420 100644 --- a/packages/nimble-components/src/dialog/tests/types.ts +++ b/packages/nimble-components/src/dialog/tests/types.ts @@ -4,3 +4,10 @@ export const ExampleContentType = { } as const; export type ExampleContentType = (typeof ExampleContentType)[keyof typeof ExampleContentType]; + +export const DialogSizeOptions = { + smallGrowable: 'Small growable', + largeFixed: 'Large growable' +} as const; +export type DialogSizeOptions = + (typeof DialogSizeOptions)[keyof typeof DialogSizeOptions]; diff --git a/packages/nimble-components/src/theme-provider/design-token-comments.ts b/packages/nimble-components/src/theme-provider/design-token-comments.ts index dae674b046..3530fe5a26 100644 --- a/packages/nimble-components/src/theme-provider/design-token-comments.ts +++ b/packages/nimble-components/src/theme-provider/design-token-comments.ts @@ -61,6 +61,15 @@ export const comments: { readonly [key in TokenName]: string | null } = { iconSize: 'Standard layout height for all icons', groupHeaderTextTransform: 'CSS text-transform string to use for headers', drawerWidth: 'TODO: delete when able', + dialogSmallWidth: + 'Standard width for small dialogs like a confirmation dialog.', + dialogSmallHeight: + 'Standard height for small dialogs like a confirmation dialog.', + dialogSmallMaxHeight: + 'Standard maximum height for small dialogs like a confirmation dialog.', + dialogLargeWidth: 'Standard width for large dialogs.', + dialogLargeHeight: 'Standard height for large dialogs.', + dialogLargeMaxHeight: 'Standard maximum height for large dialogs.', bannerGapSize: 'Space between stacked banners', spinnerSmallHeight: 'Small height (16px) for a spinner component', spinnerMediumHeight: 'Medium height (32px) for a spinner component', diff --git a/packages/nimble-components/src/theme-provider/design-token-names.ts b/packages/nimble-components/src/theme-provider/design-token-names.ts index 5ab439e6e5..24ce825650 100644 --- a/packages/nimble-components/src/theme-provider/design-token-names.ts +++ b/packages/nimble-components/src/theme-provider/design-token-names.ts @@ -47,6 +47,12 @@ export const tokenNames: { readonly [key in TokenName]: string } = { iconSize: 'icon-size', groupHeaderTextTransform: 'group-header-text-transform', drawerWidth: 'drawer-width', + dialogSmallWidth: 'dialog-small-width', + dialogSmallHeight: 'dialog-small-height', + dialogSmallMaxHeight: 'dialog-small-max-height', + dialogLargeWidth: 'dialog-large-width', + dialogLargeHeight: 'dialog-large-height', + dialogLargeMaxHeight: 'dialog-large-max-height', bannerGapSize: 'banner-gap-size', spinnerSmallHeight: 'spinner-small-height', spinnerMediumHeight: 'spinner-medium-height', @@ -249,6 +255,7 @@ const tokenSuffixes = [ 'TextTransform', 'FontFamily', 'BoxShadow', + 'MaxHeight', 'Font', 'Size', 'Width', diff --git a/packages/nimble-components/src/theme-provider/design-tokens.ts b/packages/nimble-components/src/theme-provider/design-tokens.ts index 672443952b..ccc762e764 100644 --- a/packages/nimble-components/src/theme-provider/design-tokens.ts +++ b/packages/nimble-components/src/theme-provider/design-tokens.ts @@ -306,6 +306,24 @@ export const iconSize = DesignToken.create<string>( export const drawerWidth = DesignToken.create<string>( styleNameFromTokenName(tokenNames.drawerWidth) ).withDefault('784px'); +export const dialogSmallWidth = DesignToken.create<string>( + styleNameFromTokenName(tokenNames.dialogSmallWidth) +).withDefault('400px'); +export const dialogSmallHeight = DesignToken.create<string>( + styleNameFromTokenName(tokenNames.dialogSmallHeight) +).withDefault('fit-content'); +export const dialogSmallMaxHeight = DesignToken.create<string>( + styleNameFromTokenName(tokenNames.dialogSmallMaxHeight) +).withDefault('600px'); +export const dialogLargeWidth = DesignToken.create<string>( + styleNameFromTokenName(tokenNames.dialogLargeWidth) +).withDefault('1024px'); +export const dialogLargeHeight = DesignToken.create<string>( + styleNameFromTokenName(tokenNames.dialogLargeHeight) +).withDefault('680px'); +export const dialogLargeMaxHeight = DesignToken.create<string>( + styleNameFromTokenName(tokenNames.dialogLargeMaxHeight) +).withDefault('680px'); export const bannerGapSize = DesignToken.create<string>( styleNameFromTokenName(tokenNames.bannerGapSize) ).withDefault('1px'); diff --git a/packages/nimble-components/src/theme-provider/tests/tokens.stories.ts b/packages/nimble-components/src/theme-provider/tests/tokens.stories.ts index 2bb56f9115..675dbf4a24 100644 --- a/packages/nimble-components/src/theme-provider/tests/tokens.stories.ts +++ b/packages/nimble-components/src/theme-provider/tests/tokens.stories.ts @@ -109,6 +109,7 @@ const tokenTemplates: { TextTransform: stringValueTemplate, FontFamily: stringValueTemplate, BoxShadow: stringValueTemplate, + MaxHeight: stringValueTemplate, Font: fontTemplate, Size: stringValueTemplate, Width: stringValueTemplate,