Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ffab5df
Add Experience Customization Examples
wemmyo Apr 14, 2026
7cb6958
Merge branch 'ecw-pattern' of https://github.com/jpmorganchase/salt-d…
wemmyo Apr 14, 2026
98b0227
Update integrate wizard form hook
wemmyo Apr 15, 2026
7ae189d
Sync salt provider with form
wemmyo Apr 15, 2026
048cef0
Add density images
wemmyo Apr 15, 2026
7c677d9
Add Modal examples
wemmyo Apr 15, 2026
38c368f
Link example to site resource
wemmyo Apr 15, 2026
ff87b37
Merge branch 'ecw-pattern' into ecw-examples
wemmyo Apr 15, 2026
b8074be
Add validation to modal stories
wemmyo Apr 16, 2026
f359e25
Add mandatory example
wemmyo Apr 16, 2026
e425b8e
Improve screen reader support
wemmyo Apr 16, 2026
467cd49
Refactor validation logic in useWizardForm to revalidate only if ther…
wemmyo Apr 16, 2026
3d479b1
Improve responsiveness and remove provider use
wemmyo Apr 20, 2026
87fa63a
Merge branch 'ecw-pattern' into ecw-examples
wemmyo Apr 20, 2026
d551684
Fix handleRadioChange function to enable radio button updates in wiza…
wemmyo Apr 20, 2026
db00311
Merge branch 'ecw-examples' of https://github.com/jpmorganchase/salt-…
wemmyo Apr 20, 2026
4888144
Update example naming to match docs
wemmyo Apr 21, 2026
e4f04f7
Merge branch 'ecw-pattern' into ecw-examples
wemmyo May 5, 2026
f46f5c1
Add new examples
wemmyo May 7, 2026
3945e67
Enhance experience customization components with new stock data displ…
wemmyo May 7, 2026
2b66311
Add FoundationContent component and enhance RegionalSettingsContent w…
wemmyo May 8, 2026
01e6c63
Add dark mode images
wemmyo May 8, 2026
8fe4823
Improve responsiveness
wemmyo May 8, 2026
7e255ed
Merge branch 'ecw-pattern' into ecw-examples
wemmyo May 8, 2026
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import {
Card,
Display3,
FlexItem,
FlexLayout,
FormField,
FormFieldLabel,
RadioButton,
RadioButtonGroup,
StackLayout,
Switch,
Text,
useTheme,
} from "@salt-ds/core";
import { US } from "@salt-ds/countries";
import { ArrowDownIcon, ArrowUpIcon } from "@salt-ds/icons";
import type { FormContentProps } from "./experience-customization.stories";
import NegativeTrend from "./img/negative-trend.png";
import NegativeTrendDark from "./img/negative-trend-dark.png";
import PositiveTrend from "./img/positive-trend.png";
import PositiveTrendDark from "./img/positive-trend-dark.png";

const stockCards = [
{
ticker: "VRT",
fullName: "VERTIV HOLDINGS CO-A",
exchange: "NYSE",
trendImage: PositiveTrend,
trendImageDark: PositiveTrendDark,
trendAlt: "Positive trend",
isPositive: true,
changeText: "+6.27 (+1.95%)",
changeColor: "success" as const,
metrics: {
lastPrice: "328.02",
absolute: "2.25",
marketCap: "66.199B",
},
},
{
ticker: "GEV",
fullName: "GE VERNOVA INC",
exchange: "NYSE",
trendImage: NegativeTrend,
trendImageDark: NegativeTrendDark,
trendAlt: "Negative trend",
isPositive: false,
changeText: "-4.03 (-0.35%)",
changeColor: "error" as const,
metrics: {
lastPrice: "1147.27",
absolute: "-8.92",
marketCap: "684.15B",
},
},
];

export const DataFormatContent = ({
formData,
handleRadioChange,
handleCheckboxChange,
}: FormContentProps) => {
const { mode } = useTheme();

const showExchangeText = formData.exchangeAndRegionDisplay !== "flag";
const showFlag = formData.exchangeAndRegionDisplay !== "text";
const getDisplayMetric = (stock: (typeof stockCards)[number]) => {
if (formData.visibleMetrics === "absolute") {
return stock.metrics.absolute;
}

if (formData.visibleMetrics === "marketCap") {
return stock.metrics.marketCap;
}

return stock.metrics.lastPrice;
};

return (
<FlexLayout justify="space-between" wrap>
<StackLayout>
<FormField>
<FormFieldLabel>Stock name display</FormFieldLabel>
<RadioButtonGroup
onChange={handleRadioChange}
name="stockNameDisplay"
value={formData.stockNameDisplay}
>
<RadioButton label="Ticker only" value="tickerOnly" />
<RadioButton label="Ticker and full name" value="fullNameTicker" />
</RadioButtonGroup>
</FormField>
<FormField>
<FormFieldLabel>Exchange & Region</FormFieldLabel>
<RadioButtonGroup
onChange={handleRadioChange}
name="exchangeAndRegionDisplay"
value={formData.exchangeAndRegionDisplay}
>
<RadioButton label="Text only" value="text" />
<RadioButton label="Flag only" value="flag" />
<RadioButton label="Both" value="both" />
</RadioButtonGroup>
</FormField>
<FormField>
<FormFieldLabel>Visible metrics</FormFieldLabel>
<RadioButtonGroup
onChange={handleRadioChange}
name="visibleMetrics"
value={formData.visibleMetrics}
>
<RadioButton label="Last price" value="lastPrice" />
<RadioButton label="Absolute change" value="absolute" />
<RadioButton label="Market Cap" value="marketCap" />
</RadioButtonGroup>
</FormField>

<FormField>
<FormFieldLabel>Performance chart</FormFieldLabel>
<Switch
name="performanceChart"
checked={formData.performanceChart}
onChange={handleCheckboxChange}
label={formData.performanceChart ? "Visible" : "Hidden"}
/>
</FormField>
</StackLayout>
<FlexItem>
<Card>
<StackLayout separators>
{stockCards.map((stock) => (
<FlexItem key={stock.ticker}>
<StackLayout gap={1}>
<FlexLayout justify="space-between">
<FlexItem>
<Text>
<strong>{stock.ticker}</strong>
</Text>
{formData.stockNameDisplay === "fullNameTicker" && (
<Text color="secondary">{stock.fullName}</Text>
)}
<Display3>
{getDisplayMetric(stock)}
{stock.isPositive ? (
<ArrowUpIcon
aria-hidden
style={{
color:
"var(--salt-sentiment-positive-foreground-informative)",
}}
/>
) : (
<ArrowDownIcon
aria-hidden
style={{
color:
"var(--salt-sentiment-negative-foreground-informative)",
}}
/>
)}
</Display3>
<Text color={stock.changeColor}>{stock.changeText}</Text>
</FlexItem>
<FlexItem>
<FlexLayout gap={1} align="center">
{showExchangeText && (
<Text color="secondary">{stock.exchange}</Text>
)}
{showFlag && <US />}
</FlexLayout>
</FlexItem>
</FlexLayout>

{formData.performanceChart && (
<img
src={
mode === "dark"
? stock.trendImageDark
: stock.trendImage
}
alt={stock.trendAlt}
style={{ width: "100%" }}
height="64px"
/>
)}
</StackLayout>
</FlexItem>
))}
</StackLayout>
</Card>
</FlexItem>
</FlexLayout>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {
Banner,
BannerContent,
Checkbox,
FlexItem,
FlexLayout,
FormField,
FormFieldHelperText,
InteractableCard,
InteractableCardGroup,
Link,
RadioButtonIcon,
StackLayout,
Text,
useId,
useTheme,
} from "@salt-ds/core";
import type { FormContentProps } from "./experience-customization.stories";
import HighDensityTable from "./img/table-high.png";
import HighDensityTableDark from "./img/table-high-dark.png";
import LowDensityTable from "./img/table-low.png";
import LowDensityTableDark from "./img/table-low-dark.png";
import MediumDensityTable from "./img/table-medium.png";
import MediumDensityTableDark from "./img/table-medium-dark.png";

const displayDensityOptions = [
{
value: "high",
label: "High density",
image: HighDensityTable,
darkImage: HighDensityTableDark,
alt: "High Density",
},
{
value: "medium",
label: "Medium density",
image: MediumDensityTable,
darkImage: MediumDensityTableDark,
alt: "Medium Density",
},
{
value: "low",
label: "Low density",
image: LowDensityTable,
darkImage: LowDensityTableDark,
alt: "Low Density",
},
] as const;

export const FoundationContent = ({
formData,
handleSelectChange,
stepFieldValidation,
handleCheckboxChange,
}: FormContentProps) => {
const { mode } = useTheme();
const densityId = useId();

return (
<StackLayout aria-live="polite">
{formData.displayDensity === "high" && (
<Banner status="warning">
<BannerContent>
High density doesn't meet the{" "}
<Link href="https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html">
WCAG-defined minimum target size
</Link>
, which may reduce readability and make interactions harder.
</BannerContent>
</Banner>
)}

<FlexItem>
<StackLayout gap={1}>
<Text styleAs="label" id={densityId}>
Choose a density
</Text>
<InteractableCardGroup
aria-labelledby={densityId}
value={formData.displayDensity}
onChange={(_event, value) => {
handleSelectChange?.(value as string, "displayDensity");
}}
>
<FlexLayout>
{displayDensityOptions.map((option) => (
<FlexItem key={option.value}>
<InteractableCard value={option.value}>
<StackLayout gap={1}>
<img
src={mode === "dark" ? option.darkImage : option.image}
alt=""
style={{
height: 200,
width: "100%",
objectFit: "contain",
}}
aria-hidden
/>
<StackLayout direction="row" gap={1}>
<RadioButtonIcon
aria-hidden
checked={formData.displayDensity === option.value}
/>
<StackLayout gap={0}>
<Text>{option.label}</Text>
</StackLayout>
</StackLayout>
</StackLayout>
</InteractableCard>
</FlexItem>
))}
</FlexLayout>
</InteractableCardGroup>
</StackLayout>
</FlexItem>

{formData.displayDensity === "high" && (
<FormField
necessity="required"
validationStatus={stepFieldValidation.acceptTerms?.status}
>
<Checkbox
name="acceptTerms"
label="I understand that High density reduces target sizes and may affect readability and ease of use."
checked={formData.acceptTerms}
onChange={handleCheckboxChange}
/>
{stepFieldValidation.acceptTerms?.status && (
<FormFieldHelperText>
{stepFieldValidation.acceptTerms.message}
</FormFieldHelperText>
)}
</FormField>
)}
</StackLayout>
);
};
Loading
Loading