From 6c7b84d2da5da4337192dff59c9e8c4e2f3bd3d7 Mon Sep 17 00:00:00 2001 From: Dylan Hyun Date: Tue, 23 Sep 2025 16:58:23 -0400 Subject: [PATCH 1/6] HDS-5365 Convert Form base elements showcase page to gts --- .../code-fragments/with-character-count.gts | 64 ++ .../form/base-elements/index.gts | 39 + .../sub-sections/base-elements.gts | 463 +++++++++++ .../form/base-elements/sub-sections/field.gts | 236 ++++++ .../base-elements/sub-sections/fieldset.gts | 164 ++++ .../page-components/form/base-elements.ts | 44 - .../page-components/form/base-elements.ts | 18 +- .../page-components/form/base-elements.gts | 13 + .../page-components/form/base-elements.hbs | 784 ------------------ 9 files changed, 980 insertions(+), 845 deletions(-) create mode 100644 showcase/app/components/page-components/form/base-elements/code-fragments/with-character-count.gts create mode 100644 showcase/app/components/page-components/form/base-elements/index.gts create mode 100644 showcase/app/components/page-components/form/base-elements/sub-sections/base-elements.gts create mode 100644 showcase/app/components/page-components/form/base-elements/sub-sections/field.gts create mode 100644 showcase/app/components/page-components/form/base-elements/sub-sections/fieldset.gts delete mode 100644 showcase/app/controllers/page-components/form/base-elements.ts create mode 100644 showcase/app/templates/page-components/form/base-elements.gts delete mode 100644 showcase/app/templates/page-components/form/base-elements.hbs diff --git a/showcase/app/components/page-components/form/base-elements/code-fragments/with-character-count.gts b/showcase/app/components/page-components/form/base-elements/code-fragments/with-character-count.gts new file mode 100644 index 00000000000..22887171be7 --- /dev/null +++ b/showcase/app/components/page-components/form/base-elements/code-fragments/with-character-count.gts @@ -0,0 +1,64 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ +import Component from '@glimmer/component'; +import { on } from '@ember/modifier'; +import { tracked } from '@glimmer/tracking'; +import { guidFor } from '@ember/object/internals'; +import type Owner from '@ember/owner'; + +import { HdsFormCharacterCount } from '@hashicorp/design-system-components/components'; + +export interface CodeFragmentWithCharacterCountSignature { + Args: { + value?: string; + ariaLabel?: string; + minLength?: number; + maxLength?: number; + customContent?: boolean; + }; + Element: HTMLDivElement; +} + +export default class CodeFragmentWithCharacterCount extends Component { + @tracked value = ''; + uuid = guidFor(this); + + constructor( + owner: Owner, + args: CodeFragmentWithCharacterCountSignature['Args'], + ) { + super(owner, args); + this.value = this.args.value ?? ''; + } + + updateValue = (event: Event) => { + const { value } = event.target as HTMLInputElement; + this.value = value; + }; + + +} diff --git a/showcase/app/components/page-components/form/base-elements/index.gts b/showcase/app/components/page-components/form/base-elements/index.gts new file mode 100644 index 00000000000..817d296feaf --- /dev/null +++ b/showcase/app/components/page-components/form/base-elements/index.gts @@ -0,0 +1,39 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { pageTitle } from 'ember-page-title'; + +import ShwTextH1 from 'showcase/components/shw/text/h1'; + +import SubSectionBaseElements from 'showcase/components/page-components/form/base-elements/sub-sections/base-elements'; +import SubSectionField from 'showcase/components/page-components/form/base-elements/sub-sections/field'; +import SubSectionFieldset from 'showcase/components/page-components/form/base-elements/sub-sections/fieldset'; + +export default class FormBaseElementsIndex extends Component { + @tracked showHighlight = false; + + toggleHighlight = () => { + this.showHighlight = !this.showHighlight; + }; + + +} diff --git a/showcase/app/components/page-components/form/base-elements/sub-sections/base-elements.gts b/showcase/app/components/page-components/form/base-elements/sub-sections/base-elements.gts new file mode 100644 index 00000000000..3bbfba4f55a --- /dev/null +++ b/showcase/app/components/page-components/form/base-elements/sub-sections/base-elements.gts @@ -0,0 +1,463 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ +import type { TemplateOnlyComponent } from '@ember/component/template-only'; +import style from 'ember-style-modifier'; + +import ShwDivider from 'showcase/components/shw/divider'; +import ShwFlex from 'showcase/components/shw/flex'; +import ShwGrid from 'showcase/components/shw/grid'; +import ShwTextBody from 'showcase/components/shw/text/body'; +import ShwTextH2 from 'showcase/components/shw/text/h2'; +import ShwTextH3 from 'showcase/components/shw/text/h3'; +import ShwOutliner from 'showcase/components/shw/outliner'; + +import CodeFragmentWithCharacterCount from 'showcase/components/page-components/form/base-elements/code-fragments/with-character-count'; + +import { + HdsBadge, + HdsFormError, + HdsFormHelperText, + HdsFormIndicator, + HdsFormLabel, + HdsFormLegend, + HdsFormVisibilityToggle, + HdsLinkInline, +} from '@hashicorp/design-system-components/components'; + +const SubSectionBaseElements: TemplateOnlyComponent = ; + +export default SubSectionBaseElements; diff --git a/showcase/app/components/page-components/form/base-elements/sub-sections/field.gts b/showcase/app/components/page-components/form/base-elements/sub-sections/field.gts new file mode 100644 index 00000000000..95d46d6266c --- /dev/null +++ b/showcase/app/components/page-components/form/base-elements/sub-sections/field.gts @@ -0,0 +1,236 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ +import type { TemplateOnlyComponent } from '@ember/component/template-only'; +import { array } from '@ember/helper'; +import { capitalize } from '@ember/string'; +import { eq } from 'ember-truth-helpers'; +import { on } from '@ember/modifier'; +import style from 'ember-style-modifier'; + +import ShwDivider from 'showcase/components/shw/divider'; +import ShwGrid from 'showcase/components/shw/grid'; +import ShwTextH2 from 'showcase/components/shw/text/h2'; +import ShwTextH3 from 'showcase/components/shw/text/h3'; +import ShwPlaceholder from 'showcase/components/shw/placeholder'; + +import { LAYOUT_TYPES } from '@hashicorp/design-system-components/components/hds/form/field/index'; + +import { HdsFormField } from '@hashicorp/design-system-components/components'; + +export interface SubSectionFieldSignature { + Args: { + showHighlight: boolean; + toggleHighlight: () => void; + }; +} + +const SubSectionField: TemplateOnlyComponent = + ; + +export default SubSectionField; diff --git a/showcase/app/components/page-components/form/base-elements/sub-sections/fieldset.gts b/showcase/app/components/page-components/form/base-elements/sub-sections/fieldset.gts new file mode 100644 index 00000000000..e14789603f1 --- /dev/null +++ b/showcase/app/components/page-components/form/base-elements/sub-sections/fieldset.gts @@ -0,0 +1,164 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ +import type { TemplateOnlyComponent } from '@ember/component/template-only'; +import { array } from '@ember/helper'; +import { capitalize } from '@ember/string'; +import { on } from '@ember/modifier'; +import style from 'ember-style-modifier'; + +import ShwDivider from 'showcase/components/shw/divider'; +import ShwFlex from 'showcase/components/shw/flex'; +import ShwGrid from 'showcase/components/shw/grid'; +import ShwTextH2 from 'showcase/components/shw/text/h2'; +import ShwTextH3 from 'showcase/components/shw/text/h3'; +import ShwPlaceholder from 'showcase/components/shw/placeholder'; + +import { LAYOUT_TYPES } from '@hashicorp/design-system-components/components/hds/form/fieldset/index'; + +import { HdsFormFieldset } from '@hashicorp/design-system-components/components'; + +export interface SubSectionFieldsetSignature { + Args: { + showHighlight: boolean; + toggleHighlight: () => void; + }; +} + +const SubSectionFieldset: TemplateOnlyComponent = + ; + +export default SubSectionFieldset; diff --git a/showcase/app/controllers/page-components/form/base-elements.ts b/showcase/app/controllers/page-components/form/base-elements.ts deleted file mode 100644 index 30fe2e832b5..00000000000 --- a/showcase/app/controllers/page-components/form/base-elements.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: MPL-2.0 - */ - -import Controller from '@ember/controller'; -import { action } from '@ember/object'; -import { tracked } from '@glimmer/tracking'; -import { deepTracked } from 'ember-deep-tracked'; - -import type { PageComponentsFormBaseElementsModel } from 'showcase/routes/page-components/form/base-elements'; - -export default class PageComponentsFormBaseElementsController extends Controller { - declare model: PageComponentsFormBaseElementsModel; - - @tracked showHighlight = false; - @deepTracked values = { - value1: '', - value2: 'cl', - value3: '', - value4: 'cluster', - value5: 'cluster-length-is-longer-', - value6: 'cluster-length-is-longer-than', - value7: '', - value8: 'c', - value9: 'clu', - value10: '', - value11: 'c', - value12: 'cluster', - value13: 'cluster-length-is-longer-than', - value14: 'Lorem ipsum dolor', - }; - - @action - toggleHighlight() { - this.showHighlight = !this.showHighlight; - } - - @action - updateValue(propName: keyof typeof this.values, event: Event) { - const { value } = event.target as HTMLInputElement; - this.values[propName] = value; - } -} diff --git a/showcase/app/routes/page-components/form/base-elements.ts b/showcase/app/routes/page-components/form/base-elements.ts index 24173efec05..d84ccd4df45 100644 --- a/showcase/app/routes/page-components/form/base-elements.ts +++ b/showcase/app/routes/page-components/form/base-elements.ts @@ -5,25 +5,9 @@ import Route from '@ember/routing/route'; -import { LAYOUT_TYPES as FIELD_LAYOUT_TYPES } from '@hashicorp/design-system-components/components/hds/form/field/index'; -import { LAYOUT_TYPES as FIELDSET_LAYOUT_TYPES } from '@hashicorp/design-system-components/components/hds/form/fieldset/index'; - import type { ModelFrom } from 'showcase/utils/ModelFromRoute'; export type PageComponentsFormBaseElementsModel = ModelFrom; -export default class PageComponentsFormBaseElementsRoute extends Route { - model() { - // these are used only for presentation purpose in the showcase - const SAMPLE_ERROR_MESSAGES = [ - 'First error message', - 'Second error message', - ]; - return { - SAMPLE_ERROR_MESSAGES, - FIELD_LAYOUT_TYPES, - FIELDSET_LAYOUT_TYPES, - }; - } -} +export default class PageComponentsFormBaseElementsRoute extends Route {} diff --git a/showcase/app/templates/page-components/form/base-elements.gts b/showcase/app/templates/page-components/form/base-elements.gts new file mode 100644 index 00000000000..5fe3548a6b2 --- /dev/null +++ b/showcase/app/templates/page-components/form/base-elements.gts @@ -0,0 +1,13 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ +import type { TemplateOnlyComponent } from '@ember/component/template-only'; + +import FormBaseElementsIndex from 'showcase/components/page-components/form/base-elements'; + +const PageComponentsFormBaseElements: TemplateOnlyComponent = ; + +export default PageComponentsFormBaseElements; diff --git a/showcase/app/templates/page-components/form/base-elements.hbs b/showcase/app/templates/page-components/form/base-elements.hbs deleted file mode 100644 index 6f48727d16a..00000000000 --- a/showcase/app/templates/page-components/form/base-elements.hbs +++ /dev/null @@ -1,784 +0,0 @@ -{{! - Copyright (c) HashiCorp, Inc. - SPDX-License-Identifier: MPL-2.0 -}} - -{{page-title "Form / Base elements"}} - -Form / Base elements - -
- - Label - - - - This is a simple label - - - This is the label - - - This is the label - - - With structured content (eg. a flex layout and a <Badge>) - -
- This is the label - -
-
-
- - With structured content (eg. a - flex - layout and a - <Badge>) and required indicator - -
- This is the label - -
-
-
- - With structured content (eg. a - flex - layout and a - <Badge>) and optional indicator - - - This is the label - - - - - - - This is a very long label text that should go on multiple lines - - - - - This is a very long label text that should go on multiple lines - - - - - This is a very long label text that should go on multiple lines - - - - With text that spans multiple lines, structured content (eg. a - flex - layout and a - <Badge>) and required indicator - - - - This is a very long label text that should go on multiple lines - - - - -
- - {{! ###################### }} - - - - Helper text - - - - This is the helper text, usually used jointly with the label. - - - With <Link::Inline> - This is a helper text - with a link - - - With <Link::Inline> and secondary color - This is a helper text - with a secondary link - - - - A helper text may contain some - <code> - for example, or a - <strong>. - - - - - This is a very long helper text that should go on multiple lines - - - - - {{! ###################### }} - - - - Indicator - - - - - - - - - - - - - - {{! ###################### }} - - - - Character count - - Default content - - Base - - {{! template-lint-disable require-input-label }} - - - - - - - - - - - {{! template-lint-enable require-input-label }} - - maxLength - - {{! template-lint-disable require-input-label }} - - - {{#let (unique-id) as |controlId|}} - - - {{/let}} - - - {{#let (unique-id) as |controlId|}} - - - {{/let}} - - - {{#let (unique-id) as |controlId|}} - - - {{/let}} - - - {{#let (unique-id) as |controlId|}} - - - {{/let}} - - - {{! template-lint-enable require-input-label }} - - minLength - - {{! template-lint-disable require-input-label }} - - - {{#let (unique-id) as |controlId|}} - - - {{/let}} - - - {{#let (unique-id) as |controlId|}} - - - {{/let}} - - - {{#let (unique-id) as |controlId|}} - - - {{/let}} - - - - {{! template-lint-enable require-input-label }} - - minLength + maxLength - - {{! template-lint-disable require-input-label }} - - - {{#let (unique-id) as |controlId|}} - - - {{/let}} - - - {{#let (unique-id) as |controlId|}} - - - {{/let}} - - - {{#let (unique-id) as |controlId|}} - - - {{/let}} - - - {{#let (unique-id) as |controlId|}} - - - {{/let}} - - - {{! template-lint-enable require-input-label }} - - - - Custom content - - {{! template-lint-disable require-input-label }} - - - {{#let (unique-id) as |controlId|}} - - - maxLength={{CC.maxLength}}; minLength={{CC.minLength}}; remaining={{CC.remaining}}; shortfall={{CC.shortfall}}; - currentLength={{CC.currentLength}}; - - {{/let}} - - - {{! template-lint-enable require-input-label }} - - {{! ###################### }} - - - - Error - - - - This is a simple error message - - - - This is a very long error message that should span on multiple lines - - - - - {{#each @model.SAMPLE_ERROR_MESSAGES as |message|}} - {{message}} - {{/each}} - - - - - {{! ###################### }} - - - - Legend - - - - This is a simple legend - - - With <Link::Inline> - This is a legend - with a link - - - With <Link::Inline> and secondary color - This is a legend - with a secondary link - - - This is a simple legend - - - This is a simple legend - - - With structured content (eg. a - flex - layout and a - <Badge>) - - -
This is the legend - -
-
-
- - With structured content (eg. a - flex - layout and a - <Badge>) and required indicator - -
- - This is the legend - - -
-
- - With structured content (eg. a - flex - layout and a - <Badge>) and optional indicator - -
- - This is the legend - - -
-
- - - This is a very long legend text that should go on multiple lines - - - - - This is a very long legend text that should go on multiple lines - - - - - This is a very long legend text that should go on multiple lines - - - - With text that spans multiple lines, structured content (eg. a - flex - layout and a - <Badge>) and required indicator - - - - This is a very long legend text that should go on multiple lines - - - - -
- - {{! ###################### }} - - - - Field - - - -
- - Layout - - - {{#each @model.FIELD_LAYOUT_TYPES as |layout|}} - - - This is the label - This is the helper text - - {{#if (eq layout "vertical")}} - - {{/if}} - {{#if (eq layout "flag")}} - - {{/if}} - - This is the error - - - {{/each}} - - - {{#each @model.FIELD_LAYOUT_TYPES as |layout|}} - - Content / "{{layout}}" layout - - - - - This is the label text - - {{#if (eq layout "vertical")}} - - {{/if}} - {{#if (eq layout "flag")}} - - {{/if}} - - - - - - This is the label text - This is the helper text - - {{#if (eq layout "vertical")}} - - {{/if}} - {{#if (eq layout "flag")}} - - {{/if}} - - - - - - This is the label text - This is the helper text - - {{#if (eq layout "vertical")}} - - {{/if}} - {{#if (eq layout "flag")}} - - {{/if}} - - - - - -
- - - - - This is the label - - {{#if (eq layout "vertical")}} - - {{/if}} - {{#if (eq layout "flag")}} - - {{/if}} - - This is the error - - - - - This is the label - This is the helper text - - {{#if (eq layout "vertical")}} - - {{/if}} - {{#if (eq layout "flag")}} - - {{/if}} - - This is the error - - - - - This is the label - This is the helper text - - {{#if (eq layout "vertical")}} - - {{/if}} - {{#if (eq layout "flag")}} - - {{/if}} - - - First error message - Second error message - - - - - {{/each}} - - {{#each @model.FIELD_LAYOUT_TYPES as |layout|}} - - Containers / "{{layout}}" layout - - {{#let (array "block" "flex" "grid") as |displays|}} - {{#each displays as |display|}} - - Parent with display: {{display}} -
- - This is the label - This is the helper text - - {{#if (eq layout "vertical")}} - - {{/if}} - {{#if (eq layout "flag")}} - - {{/if}} - - This is the error - -
-
-
- - This is the label text that should go on multiple lines - This is the helper text that should go on multiple lines - - {{#if (eq layout "vertical")}} - - {{/if}} - {{#if (eq layout "flag")}} - - {{/if}} - - - This is the first error text - This is the second error text that should go on multiple lines - - -
-
- {{/each}} - {{/let}} -
- {{/each}} -
- - {{! ###################### }} - - - - Fieldset - - - -
- - Layout - - {{#each @model.FIELDSET_LAYOUT_TYPES as |layout|}} - - - This is the legend - This is the helper text - - - - - - - - - - This is the error - - - {{/each}} - - - {{#each @model.FIELDSET_LAYOUT_TYPES as |layout|}} - - Containers / "{{layout}}" layout - - {{#let (array "block" "flex" "grid") as |displays|}} - {{#each displays as |display|}} - - Parent with display: {{display}} -
- - This is the legend - This is the helper text - - - - - - - This is the error - -
-
-
- - This is the legend text that should go on multiple lines - This is the helper text that should go on multiple lines - - - - - - - - This is the first error text - This is the second error text that should go on multiple lines - - -
-
- {{/each}} - {{/let}} -
- {{/each}} -
- - {{! ###################### }} - - - - Visibility toggle - - - - - - - - - - - - - -
\ No newline at end of file From c147d43a7a5ccd40c1a4edaeac414559ca109f29 Mon Sep 17 00:00:00 2001 From: Dylan Hyun Date: Tue, 23 Sep 2025 17:14:07 -0400 Subject: [PATCH 2/6] Fix: Add back toggle button ids --- .../page-components/form/base-elements/sub-sections/field.gts | 1 + .../page-components/form/base-elements/sub-sections/fieldset.gts | 1 + 2 files changed, 2 insertions(+) diff --git a/showcase/app/components/page-components/form/base-elements/sub-sections/field.gts b/showcase/app/components/page-components/form/base-elements/sub-sections/field.gts index 95d46d6266c..164b1f0db0c 100644 --- a/showcase/app/components/page-components/form/base-elements/sub-sections/field.gts +++ b/showcase/app/components/page-components/form/base-elements/sub-sections/field.gts @@ -31,6 +31,7 @@ const SubSectionField: TemplateOnlyComponent = Field