-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
278 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
```hbs template | ||
<Form::Fieldset | ||
@label='Label' | ||
@hint='Extra information about the fieldset' | ||
@error='Error message' | ||
> | ||
<p class='text-body-and-labels text-xs m-0 italic'>~Fieldset components render | ||
here!~</p> | ||
</Form::Fieldset> | ||
``` | ||
|
||
```js component | ||
import Component from '@glimmer/component'; | ||
|
||
export default class extends Component {} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# Fieldset | ||
|
||
Fieldset is a component to aid in creating form components that require an underlying `<fieldset>` and `<legend>`. It is similar to Field, in that it provides an opinionated shell for building other components such as checkbox groups and radio groups. | ||
|
||
## Label | ||
|
||
Provide a string to `@label` to render the text into the `<legend>` of the Fieldset. This is required. | ||
|
||
```hbs | ||
<Form::Fieldset @label='Label' /> | ||
``` | ||
|
||
## Hint | ||
|
||
Provide a string to `@hint` to render the text into the Hint section of the Fieldset. This is optional. | ||
|
||
```hbs | ||
<Form::Fieldset @label='Label' @hint='Hint' /> | ||
``` | ||
|
||
## Error | ||
|
||
Provide a string to `@error` to render the text into the Error section of the Fieldset. This is optional. | ||
|
||
```hbs | ||
<Form::Fieldset @label='Label' @error='Error' /> | ||
``` | ||
|
||
## Disabled State | ||
|
||
Set the `@isDisabled` argument to disable the fieldset. When disabled, all form controls that are descendants of the fieldset, are disabled, meaning they are not editable and won't be submitted along with the form. Learn more via the [fieldset documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset#attributes). | ||
|
||
```hbs | ||
<Form::Fieldset @label='Label' @isDisabled={{true}}> | ||
<!-- This is now disabled as well --> | ||
<input /> | ||
</Form::Fieldset> | ||
``` | ||
|
||
## Attributes and Modifiers | ||
|
||
Consumers have direct access to the underlying [fieldset element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset), so all attributes are supported. | ||
|
||
```hbs | ||
<Form::Fieldset @label='Label' name='my-checkboxes' data-fieldset /> | ||
``` | ||
|
||
## Test Selectors | ||
|
||
### Root Element | ||
|
||
The wrapping element is a `<fieldset>` and attributes are spread directly on it as mentioned above. Due to that, one can target the fieldset with any data attribute. | ||
|
||
```hbs | ||
<Form::Fieldset @label='Label' data-fieldset /> | ||
``` | ||
|
||
### Label | ||
|
||
Target the label element via `data-label`. | ||
|
||
### Hint | ||
|
||
Target the hint block via `data-hint`. | ||
|
||
### Wrapping Content Container | ||
|
||
The `yield` is wrapped in a div container that can be targeted with `data-control`. | ||
|
||
### Error | ||
|
||
Target the error block via `data-error`. | ||
|
||
## All UI States | ||
|
||
<div class="flex flex-col space-y-4" style="max-width: 14rem"> | ||
<Form::Fieldset @label='Label'> | ||
|
||
<p class='text-body-and-labels text-xs m-0 italic'>~Fieldset components render here!~</p> | ||
</Form::Fieldset> | ||
|
||
<Form::Fieldset @label='Label' @hint="With hint text"> | ||
|
||
<p class='text-body-and-labels text-xs m-0 italic'>~Fieldset components render here!~</p> | ||
</Form::Fieldset> | ||
|
||
<Form::Fieldset @label='Label' @error="With error"> | ||
|
||
<p class='text-body-and-labels text-xs m-0 italic'>~Fieldset components render here!~</p> | ||
</Form::Fieldset> | ||
|
||
<Form::Fieldset @label='Label' @hint="With hint text" @error="With error"> | ||
|
||
<p class='text-body-and-labels text-xs m-0 italic'>~Fieldset components render here!~</p> | ||
</Form::Fieldset> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<Form::Field as |field|> | ||
<fieldset | ||
aria-describedby="{{if @error field.errorId}} {{if @hint field.hintId}}" | ||
disabled={{@isDisabled}} | ||
...attributes | ||
> | ||
<legend | ||
class="type-md-tight text-body-and-labels block" | ||
data-label | ||
>{{@label}}</legend> | ||
|
||
{{#if @hint}} | ||
<field.Hint id={{field.hintId}} data-hint>{{@hint}}</field.Hint> | ||
{{/if}} | ||
|
||
<div | ||
class="mt-2 flex flex-col rounded-sm {{if @error 'shadow-error-outline'}}" | ||
data-control | ||
> | ||
{{yield}} | ||
</div> | ||
|
||
{{#if @error}} | ||
<field.Error id={{field.errorId}} data-error>{{@error}}</field.Error> | ||
{{/if}} | ||
</fieldset> | ||
</Form::Field> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import Component from '@glimmer/component'; | ||
import { assert } from '@ember/debug'; | ||
|
||
interface ToucanFormFieldsetComponentSignature { | ||
Element: HTMLFieldSetElement; | ||
Args: { | ||
error?: string; | ||
hasError?: boolean; | ||
hint?: string; | ||
isDisabled?: boolean; | ||
label: string; | ||
}; | ||
Blocks: { | ||
default: []; | ||
}; | ||
} | ||
|
||
export default class ToucanFormFieldComponent extends Component<ToucanFormFieldsetComponentSignature> { | ||
constructor( | ||
owner: unknown, | ||
args: ToucanFormFieldsetComponentSignature['Args'] | ||
) { | ||
assert('A "@label" argument is required', args.label); | ||
super(owner, args); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
110 changes: 110 additions & 0 deletions
110
test-app/tests/integration/components/fieldset-test.gts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/* eslint-disable no-undef -- Until https://github.com/ember-cli/eslint-plugin-ember/issues/1747 is resolved... */ | ||
/* eslint-disable simple-import-sort/imports,padding-line-between-statements,decorator-position/decorator-position -- Can't fix these manually, without --fix working in .gts */ | ||
|
||
import { find, render, setupOnerror } from '@ember/test-helpers'; | ||
import { module, test } from 'qunit'; | ||
|
||
import Fieldset from '@crowdstrike/ember-toucan-core/components/form/fieldset'; | ||
import { setupRenderingTest } from 'test-app/tests/helpers'; | ||
|
||
module('Integration | Component | Fieldset', function (hooks) { | ||
setupRenderingTest(hooks); | ||
|
||
test('it renders', async function (assert) { | ||
await render(<template> | ||
<Fieldset @label="Label" data-fieldset /> | ||
</template>); | ||
|
||
assert.dom('[data-label]').hasText('Label'); | ||
|
||
assert | ||
.dom('[data-hint]') | ||
.doesNotExist( | ||
'Expected hint block not to be displayed as a hint was not provided' | ||
); | ||
|
||
assert | ||
.dom('[data-error]') | ||
.doesNotExist( | ||
'Expected error block not to be displayed as an error was not provided' | ||
); | ||
|
||
assert.dom('[data-control]').hasNoClass('shadow-error-outline'); | ||
}); | ||
|
||
test('it renders with a hint', async function (assert) { | ||
await render(<template> | ||
<Fieldset @label="Label" @hint="Hint text" data-fieldset /> | ||
</template>); | ||
|
||
assert.dom('[data-hint]').hasText('Hint text'); | ||
assert.dom('[data-hint]').hasAttribute('id'); | ||
assert.dom('[data-fieldset]').hasAttribute('aria-describedby'); | ||
}); | ||
|
||
test('it renders with an error', async function (assert) { | ||
await render(<template> | ||
<Fieldset @label="Label" @error="Error text" data-fieldset /> | ||
</template>); | ||
|
||
assert.dom('[data-error]').hasText('Error text'); | ||
assert.dom('[data-error]').hasAttribute('id'); | ||
|
||
assert.dom('[data-fieldset]').hasAttribute('aria-describedby'); | ||
|
||
assert.dom('[data-control]').hasClass('shadow-error-outline'); | ||
}); | ||
|
||
test('it sets aria-describedby when both a hint and error are provided', async function (assert) { | ||
await render(<template> | ||
<Fieldset | ||
@label="Label" | ||
@error="Error text" | ||
@hint="Hint text" | ||
data-fieldset | ||
/> | ||
</template>); | ||
|
||
let errorId = find('[data-error]')?.getAttribute('id') || ''; | ||
assert.ok(errorId, 'Expected errorId to be truthy'); | ||
|
||
let hintId = find('[data-hint]')?.getAttribute('id') || ''; | ||
assert.ok(hintId, 'Expected hintId to be truthy'); | ||
|
||
assert | ||
.dom('[data-fieldset]') | ||
.hasAttribute('aria-describedby', `${errorId} ${hintId}`); | ||
}); | ||
|
||
test('it disables the fieldset using `@isDisabled`', async function (assert) { | ||
await render(<template> | ||
<Fieldset @label="Label" @isDisabled={{true}} data-fieldset /> | ||
</template>); | ||
|
||
assert.dom('[data-fieldset]').isDisabled(); | ||
}); | ||
|
||
test('it spreads attributes to the underlying fieldset', async function (assert) { | ||
await render(<template> | ||
<Fieldset @label="Label" form="form-id" data-fieldset /> | ||
</template>); | ||
|
||
assert.dom('[data-fieldset]').hasAttribute('form', 'form-id'); | ||
}); | ||
|
||
test('it throws an assertion error if no `@label` is provided', async function (assert) { | ||
assert.expect(1); | ||
|
||
setupOnerror((e: Error) => { | ||
assert.ok( | ||
e.message.includes('A "@label" argument is required'), | ||
'Expected assertion error message' | ||
); | ||
}); | ||
|
||
await render(<template> | ||
{{! @glint-expect-error: we are not providing @label, so this is expected }} | ||
<Fieldset /> | ||
</template>); | ||
}); | ||
}); |