Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions pages/date-input/style-api-v2.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useState } from 'react';

import { DateInput, DateInputProps, KeyValuePairs } from '~components';

import { SimplePage } from '../app/templates';
import { getInputFieldClassNames } from '../style-api/input-field';

// The `input` slot forwards to the whole Input style API; the shared helper maps the owned states to classes.
function StyledDateInput({ value: initialValue, invalid, warning, disabled, readOnly, ...rest }: DateInputProps) {
const [value, setValue] = useState(initialValue);
return (
<DateInput
{...rest}
ariaLabel="Date input field"
value={value}
onChange={({ detail }) => setValue(detail.value)}
invalid={invalid}
warning={warning}
disabled={disabled}
readOnly={readOnly}
{...{ styleClassNames: { input: getInputFieldClassNames({ invalid, warning, disabled, readOnly }) } }}
/>
);
}

export default function () {
return (
<SimplePage title="Date input - Style API v2" screenshotArea={{}} i18n={{}}>
<KeyValuePairs
columns={2}
items={[
{
type: 'pair',
label: 'Default',
value: <StyledDateInput value="2024-03-15" />,
},
{
type: 'pair',
label: 'Placeholder',
value: <StyledDateInput value="" placeholder="YYYY/MM/DD" />,
},
{
type: 'pair',
label: 'Invalid',
value: <StyledDateInput value="2024-13-45" invalid={true} />,
},
{
type: 'pair',
label: 'Warning',
value: <StyledDateInput value="1999-01-01" warning={true} />,
},
{
type: 'pair',
label: 'Disabled',
value: <StyledDateInput value="2024-03-15" disabled={true} />,
},
{
type: 'pair',
label: 'Disabled with placeholder',
value: <StyledDateInput value="" placeholder="YYYY/MM/DD" disabled={true} />,
},
{
type: 'pair',
label: 'Read-only',
value: <StyledDateInput value="2024-03-15" readOnly={true} />,
},
{
type: 'pair',
label: 'Long localized format',
value: <StyledDateInput value="2024-03-15" format="long-localized" locale="en-US" />,
},
]}
/>
</SimplePage>
);
}
99 changes: 99 additions & 0 deletions pages/style-api/input-field.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

// Shared demo styling for components whose `input` slot forwards to Input. The field box is styled with
// Input.root, one class per owned state (invalid, warning, disabled, read-only), `:hover` and `:focus-within`
// for the interaction states; the native input with Input.input.
@use '~components/styling/input';
@use './palette' as palette;

@mixin field-box {
@include input.root(
$background: palette.$surface,
$border-width: 2px,
$border-radius: 12px,
$box-shadow: 0 2px 4px palette.$subtle
);
}

.input-root {
@include field-box;
@include input.root($border-color: palette.$border);

&:hover {
@include input.root($background: palette.$surface-alt, $border-color: palette.$border-accent);
}

&:focus-within {
@include input.root($border-color: palette.$accent, $box-shadow: 0 0 0 3px palette.$accent-active);
}
}

.input-root-invalid {
@include field-box;
@include input.root($background: palette.$error-surface, $border-color: palette.$error);

&:focus-within {
@include input.root($box-shadow: 0 0 0 3px palette.$error-hover);
}
}

.input-root-warning {
@include field-box;
@include input.root($background: palette.$warning-surface, $border-color: palette.$warning);

&:focus-within {
@include input.root($box-shadow: 0 0 0 3px palette.$warning);
}
}

.input-root-disabled {
@include field-box;
@include input.root(
$background: palette.$control-bg-disabled,
$border-color: palette.$border-disabled,
$box-shadow: none
);
}

.input-root-readonly {
@include field-box;
@include input.root($background: palette.$surface-alt, $border-color: palette.$border);

&:focus-within {
@include input.root($border-color: palette.$accent, $box-shadow: 0 0 0 3px palette.$accent-active);
}
}

.input-input {
@include input.input(
$color: palette.$text,
$padding-block: 10px,
$padding-inline: 16px,
$font-family: monospace,
$font-size: 15px,
$font-weight: 600,
$placeholder-color: palette.$accent,
$placeholder-font-size: 13px,
$placeholder-font-style: normal,
$placeholder-font-weight: 400
);

&:focus {
@include input.input($color: palette.$accent);
}

&:placeholder-shown {
@include input.input($padding-inline: 24px);
}
}

.input-input-disabled {
@include input.input($color: palette.$text-disabled);
}

.input-input-readonly {
@include input.input($color: palette.$text);
}
31 changes: 31 additions & 0 deletions pages/style-api/input-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import clsx from 'clsx';

import styles from './input-field.scss';

export interface InputFieldState {
invalid?: boolean;
warning?: boolean;
disabled?: boolean;
readOnly?: boolean;
}

// Class names for a forwarded Input in one of its owned states: one class for the field box (Input.root), one
// for the native input (Input.input). The interaction states are handled by selectors in input-field.scss.
export function getInputFieldClassNames({ invalid, warning, disabled, readOnly }: InputFieldState) {
return {
root: clsx({
[styles['input-root-invalid']]: invalid,
[styles['input-root-warning']]: warning && !invalid,
[styles['input-root-disabled']]: disabled && !invalid && !warning,
[styles['input-root-readonly']]: readOnly && !disabled && !invalid && !warning,
[styles['input-root']]: !invalid && !warning && !disabled && !readOnly,
}),
input: clsx(
styles['input-input'],
disabled && styles['input-input-disabled'],
readOnly && !disabled && styles['input-input-readonly']
),
};
}
94 changes: 94 additions & 0 deletions pages/text-filter/style-api-v2.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useState } from 'react';
import clsx from 'clsx';

import { FormField, KeyValuePairs, TextFilter, TextFilterProps } from '~components';

import { SimplePage } from '../app/templates';
import { getInputFieldClassNames } from '../style-api/input-field';

import styles from './style-api-v2.scss';

// The `input` slot forwards to the whole Input style API; the shared helper maps the owned states (disabled, and
// invalid or warning from the surrounding form field) to classes.
function StyledTextFilter({
filteringText: initialFilteringText,
disabled,
invalid,
warning,
...rest
}: Omit<TextFilterProps, 'onChange'> & { invalid?: boolean; warning?: boolean }) {
const [filteringText, setFilteringText] = useState(initialFilteringText);
const fieldClassNames = getInputFieldClassNames({ invalid, warning, disabled });
const textFilter = (
<TextFilter
{...rest}
filteringAriaLabel="Filter instances"
filteringText={filteringText}
onChange={({ detail }) => setFilteringText(detail.filteringText)}
disabled={disabled}
{...{
styleClassNames: {
input: {
...fieldClassNames,
input: clsx(fieldClassNames.input, styles['input-input-search']),
searchIcon: clsx(styles['input-search-icon'], disabled && styles['input-search-icon-disabled']),
clearButton: styles['input-clear-button'],
},
countText: styles['count-text'],
},
}}
/>
);
if (invalid || warning) {
return (
<FormField errorText={invalid ? 'Invalid filter' : undefined} warningText={warning ? 'Broad filter' : undefined}>
{textFilter}
</FormField>
);
}
return textFilter;
}

export default function () {
return (
<SimplePage title="Text filter - Style API v2" screenshotArea={{}} i18n={{}}>
<KeyValuePairs
columns={2}
items={[
{
type: 'pair',
label: 'Default with count text',
value: <StyledTextFilter filteringText="invoices" countText="18 matches" />,
},
{
type: 'pair',
label: 'Placeholder',
value: <StyledTextFilter filteringText="" filteringPlaceholder="Find instances" countText="18 matches" />,
},
{
type: 'pair',
label: 'Disabled with count text',
value: <StyledTextFilter filteringText="invoices" countText="18 matches" disabled={true} />,
},
{
type: 'pair',
label: 'Disabled with placeholder',
value: <StyledTextFilter filteringText="" filteringPlaceholder="Find instances" disabled={true} />,
},
{
type: 'pair',
label: 'Invalid',
value: <StyledTextFilter filteringText="invoices" countText="0 matches" invalid={true} />,
},
{
type: 'pair',
label: 'Warning',
value: <StyledTextFilter filteringText="in" countText="1,200 matches" warning={true} />,
},
]}
/>
</SimplePage>
);
}
40 changes: 40 additions & 0 deletions pages/text-filter/style-api-v2.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

@use '~components/styling/input';
@use '~components/styling/button';
@use '~components/styling/text-filter';
@use '../style-api/palette' as palette;

// The filter is a search input: the inline padding replaces the room the component reserves for the search icon
// and the clear button, so it is set with that room included, in every state. Added to the shared input class.
.input-input-search,
.input-input-search:placeholder-shown {
@include input.input($padding-inline: 40px);
}

// Search icon (Input.searchIcon): the disabled state is a class.
.input-search-icon {
@include input.searchIcon($color: palette.$accent);
}

.input-search-icon-disabled {
@include input.searchIcon($color: palette.$text-disabled);
}

// Clear button (Input.clearButton) forwards to Button.root.
.input-clear-button {
@include button.root($color: palette.$accent, $focus-ring-color: palette.$accent);

&:hover,
&:focus {
@include button.root($color: palette.$accent-hover);
}
}

// The count text is the one slot Text filter owns.
.count-text {
@include text-filter.countText($color: palette.$accent, $font-family: monospace, $font-size: 13px, $font-weight: 700);
}
Loading
Loading