= {},
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ renderer: any = mount,
+ validationRedesignOptIn = false
+) =>
+ renderer(
+
+
+ ,
+ {
+ attachTo: document.getElementById("enzymeContainer"),
+ }
+ );
describe("TextEditor", () => {
beforeAll(() => {
@@ -872,30 +883,6 @@ describe("TextEditor", () => {
});
describe("Character Limit", () => {
- describe("Counter", () => {
- it("displays the default text when no prop value passed", () => {
- wrapper = render();
- expect(wrapper.find(Counter).text()).toEqual("3000");
- });
-
- it("overrides the text when `characterLimit` prop passed", () => {
- wrapper = render({ characterLimit: 200 });
- expect(wrapper.find(Counter).text()).toEqual("200");
- });
-
- it("the text updates as content is added to the editor", () => {
- wrapper = render({ characterLimit: 10 });
- const textEditor = wrapper.find(TextEditor);
- const editor = wrapper.find(Editor);
- act(() => {
- textEditor
- .props()
- .onChange(addToEditorState("foo foo", editor.props()));
- });
- expect(wrapper.find(Counter).text()).toEqual("3");
- });
- });
-
it("prevents enter key press when the limit has been reached", () => {
wrapper = render({ characterLimit: 0 });
const editor = wrapper.find(Editor);
@@ -962,44 +949,107 @@ describe("TextEditor", () => {
});
describe("validation", () => {
- it.each([{ error: "error" }, { warning: "warning" }, { info: "info" }])(
- "passes the validation props to the counter and renders the icon",
+ it.each(["error", "warning", "info"])(
+ "renders the %s icon when the validationRedesignOptIn flag is not set",
(msg) => {
expect(
- render({ ...msg })
+ render({ [msg]: msg })
.find(ValidationIcon)
.exists()
- ).toEqual(true);
+ ).toBe(true);
}
);
+ });
- it("applies the expected outline when an error message is passed", () => {
- assertStyleMatch(
- {
- outline: "2px solid var(--colorsSemanticNegative500)",
- },
- render({ error: "error" }).find(StyledEditorContainer)
+ it("applies the expected outline when an error message is passed", () => {
+ assertStyleMatch(
+ {
+ outline: "2px solid var(--colorsSemanticNegative500)",
+ },
+ render({ error: "error" }).find(StyledEditorContainer)
+ );
+ });
+
+ it("applies the correct outline-offset when there is an error and the editor is focused", () => {
+ wrapper = render({ error: "error" });
+ act(() => {
+ wrapper
+ .find(Editor)
+ .props()
+ .onFocus?.({} as SyntheticEvent);
+ });
+ act(() => {
+ wrapper.update();
+ });
+
+ assertStyleMatch(
+ {
+ boxShadow:
+ "0px 0px 0px var(--borderWidth300) var(--colorsSemanticFocus500),0px 0px 0px var(--borderWidth600) var(--colorsUtilityYin090)",
+ },
+ wrapper.find(StyledEditorOutline)
+ );
+ });
+
+ describe("new validations", () => {
+ it.each(["error", "warning"])(
+ "renders the new message and layout when the validationRedesignOptIn flag is set",
+ (msg) => {
+ wrapper = render({ [msg]: msg }, mount, true);
+ expect(wrapper.find(ValidationIcon).exists()).toBe(false);
+ expect(wrapper.find(ErrorBorder).exists()).toBe(true);
+ expect(wrapper.find(StyledValidationMessage).text()).toBe(msg);
+ }
+ );
+
+ it('passes id to the "aria-describedby" of the editor element when there is an error', () => {
+ wrapper = render({ error: "bar" }, mount, true);
+
+ expect(wrapper.find(Editor).prop("ariaDescribedBy")).toBe(
+ "guid-12345-validation guid-12345"
+ );
+ expect(wrapper.find(Editor).prop("ariaLabelledBy")).toBe(
+ "guid-12345-label"
);
});
- it("applies the correct outline-offset when there is an error and the editor is focused", () => {
- wrapper = render({ error: "error" });
- act(() => {
- wrapper
- .find(Editor)
- .props()
- .onFocus?.({} as SyntheticEvent);
- });
- act(() => {
- wrapper.update();
- });
+ it('passes id to the "aria-describedby" of the editor element when there is a warning', () => {
+ wrapper = render({ warning: "bar" }, mount, true);
- assertStyleMatch(
- {
- boxShadow:
- "0px 0px 0px var(--borderWidth300) var(--colorsSemanticFocus500),0px 0px 0px var(--borderWidth600) var(--colorsUtilityYin090)",
- },
- wrapper.find(StyledEditorOutline)
+ expect(wrapper.find(Editor).prop("ariaDescribedBy")).toBe(
+ "guid-12345-validation guid-12345"
+ );
+ expect(wrapper.find(Editor).prop("ariaLabelledBy")).toBe(
+ "guid-12345-label"
+ );
+ });
+ });
+
+ describe("with inputHint", () => {
+ it("renders the element as expected", () => {
+ wrapper = render({ inputHint: "foo" });
+ expect(wrapper.find(StyledHintText).text()).toEqual("foo");
+ });
+
+ it('passes id to the "aria-describedby" of the editor element when there is an error', () => {
+ wrapper = render({ error: "bar", inputHint: "foo" }, mount, true);
+
+ expect(wrapper.find(Editor).prop("ariaDescribedBy")).toBe(
+ "guid-12345-validation guid-12345-hint guid-12345"
+ );
+ expect(wrapper.find(Editor).prop("ariaLabelledBy")).toBe(
+ "guid-12345-label"
+ );
+ });
+
+ it('passes id to the "aria-describedby" of the editor element when there is a warning', () => {
+ wrapper = render({ warning: "bar", inputHint: "foo" }, mount, true);
+
+ expect(wrapper.find(Editor).prop("ariaDescribedBy")).toBe(
+ "guid-12345-validation guid-12345-hint guid-12345"
+ );
+ expect(wrapper.find(Editor).prop("ariaLabelledBy")).toBe(
+ "guid-12345-label"
);
});
});
diff --git a/src/components/text-editor/text-editor.stories.mdx b/src/components/text-editor/text-editor.stories.mdx
index c42a7aa633..1aabb032e4 100644
--- a/src/components/text-editor/text-editor.stories.mdx
+++ b/src/components/text-editor/text-editor.stories.mdx
@@ -106,6 +106,10 @@ With use of `template strings` it is possible to pass multiline validation messa
/>
+
+
### With custom row height
The `rows` prop allows for overriding the default min-height of the `TextEditor`. It accepts any number greater than 2
diff --git a/src/components/text-editor/text-editor.stories.tsx b/src/components/text-editor/text-editor.stories.tsx
index 2b821e3f2c..9cbfd6e9dc 100644
--- a/src/components/text-editor/text-editor.stories.tsx
+++ b/src/components/text-editor/text-editor.stories.tsx
@@ -6,11 +6,13 @@ import TextEditor, {
} from "./text-editor.component";
import Button from "../button";
import EditorLinkPreview from "../link-preview";
+import Box from "../box";
+import CarbonProvider from "../carbon-provider";
export const Default = () => {
const [value, setValue] = useState(EditorState.createEmpty());
return (
-
+
{
setValue(newValue);
@@ -18,7 +20,7 @@ export const Default = () => {
value={value}
labelText="Text Editor Label"
/>
-
+
);
};
@@ -31,7 +33,7 @@ export const WithContent = () => {
)
);
return (
-
+
{
setValue(newValue);
@@ -40,7 +42,7 @@ export const WithContent = () => {
labelText="Text Editor Label"
required
/>
-
+
);
};
@@ -48,15 +50,13 @@ WithContent.storyName = "with content";
export const WithOptionalButtons = () => {
const [value, setValue] = useState(EditorState.createEmpty());
- const ref = useRef(null);
return (
-
+
{
setValue(newValue);
}}
value={value}
- ref={ref}
toolbarElements={[
+
);
};
@@ -82,19 +82,17 @@ WithOptionalButtons.storyName = "with optional buttons";
export const WithOptionalCharacterLimit = () => {
const [value, setValue] = useState(EditorState.createEmpty());
const limit = 100;
- const ref = useRef(null);
return (
-
+
{
setValue(newValue);
}}
value={value}
- ref={ref}
labelText="Text Editor Label"
characterLimit={limit}
/>
-
+
);
};
@@ -106,24 +104,21 @@ export const WithValidation = () => {
);
const limit = 16;
const contentLength = value.getCurrentContent().getPlainText().length;
- const ref = useRef(null);
return (
-
+
{
setValue(newValue);
}}
value={value}
- ref={ref}
labelText="Text Editor Label"
characterLimit={limit}
error={limit - contentLength <= 5 ? "There is an error" : undefined}
- warning={
- limit - contentLength <= 10 ? "There is an warning" : undefined
- }
+ warning={limit - contentLength <= 10 ? "There is a warning" : undefined}
info={limit - contentLength <= 15 ? "There is an info" : undefined}
+ inputHint="Some additional hint text"
/>
-
+
);
};
@@ -135,7 +130,6 @@ export const WithMultilineValidation = () => {
);
const limit = 16;
const contentLength = value.getCurrentContent().getPlainText().length;
- const ref = useRef(null);
const error =
limit - contentLength <= 5
? `There is an error.
@@ -143,28 +137,56 @@ The content is too long.
Maybe try writing a little bit less?`
: undefined;
return (
-
+
{
setValue(newValue);
}}
value={value}
- ref={ref}
labelText="Text Editor Label"
characterLimit={limit}
error={error}
/>
-
+
);
};
WithMultilineValidation.storyName = "with multiline validation";
+export const WithNewValidation = () => {
+ const [value, setValue] = useState(
+ EditorState.createWithContent(ContentState.createFromText("Add content"))
+ );
+ const limit = 16;
+ const contentLength = value.getCurrentContent().getPlainText().length;
+ return (
+
+
+ {
+ setValue(newValue);
+ }}
+ value={value}
+ labelText="Text Editor Label"
+ characterLimit={limit}
+ error={limit - contentLength <= 5 ? "There is an error" : undefined}
+ warning={
+ limit - contentLength <= 10 ? "There is a warning" : undefined
+ }
+ inputHint="Some additional hint text"
+ />
+
+
+ );
+};
+
+WithNewValidation.storyName = "with new validation";
+
export const WithCustomRowHeight = () => {
const [value, setValue] = useState(EditorState.createEmpty());
return (
-
+
{
setValue(newValue);
@@ -173,7 +195,7 @@ export const WithCustomRowHeight = () => {
labelText="Text Editor Label"
rows={2}
/>
-
+
);
};
@@ -228,7 +250,7 @@ export const WithLinkPreviews = () => {
}
};
return (
-
+
{
setValue(newValue);
@@ -239,6 +261,6 @@ export const WithLinkPreviews = () => {
previews={previews.current}
onLinkAdded={addUrl}
/>
-
+
);
};
diff --git a/src/components/typography/typography.component.tsx b/src/components/typography/typography.component.tsx
index 6b3bbad456..930ca37a43 100644
--- a/src/components/typography/typography.component.tsx
+++ b/src/components/typography/typography.component.tsx
@@ -19,6 +19,7 @@ const VARIANT_TYPES = [
"segment-subheader",
"segment-subheader-alt",
"p",
+ "span",
"small",
"big",
"sup",
@@ -57,6 +58,8 @@ export interface TypographyProps extends SpaceProps, TagProps {
whiteSpace?: string;
/** Override the word-wrap */
wordWrap?: string;
+ /** Override the text-align */
+ textAlign?: string;
/** Override the text-overflow */
textOverflow?: string;
/** Apply truncation */
@@ -90,6 +93,7 @@ export const Typography = ({
listStyleType,
whiteSpace,
wordWrap,
+ textAlign,
textOverflow,
truncate,
color = "blackOpacity90",
@@ -115,6 +119,7 @@ export const Typography = ({
listStyleType={listStyleType}
whiteSpace={whiteSpace}
wordWrap={wordWrap}
+ textAlign={textAlign}
textOverflow={textOverflow}
truncate={truncate}
color={color}
diff --git a/src/components/typography/typography.spec.tsx b/src/components/typography/typography.spec.tsx
index dc5bac853f..8100c39743 100644
--- a/src/components/typography/typography.spec.tsx
+++ b/src/components/typography/typography.spec.tsx
@@ -387,6 +387,24 @@ describe("Typography", () => {
{ variant: "em" }
);
});
+ it("applies span styling", () => {
+ assert(
+ {
+ fontSize: "14px",
+ as: "span",
+ lineHeight: "21px",
+ fontWeight: "400",
+ textTransform: "none",
+ textDecoration: "none",
+ verticalAlign: undefined,
+ color: "rgba(0,0,0,0.90)",
+ display: undefined,
+ padding: "0",
+ margin: "0",
+ },
+ { variant: "span" }
+ );
+ });
});
describe("overrides", () => {
@@ -569,6 +587,24 @@ describe("Typography", () => {
}
);
+ it.each(["left", "right", "center", "justify", "initial", "inherit"])(
+ "applies text-align of %s",
+ (prop) => {
+ const wrapper = mount(
+
+ FooBar
+
+ );
+
+ assertStyleMatch(
+ {
+ textAlign: prop,
+ },
+ wrapper.find(Typography)
+ );
+ }
+ );
+
it.each(["clip", "ellipsis", "string", "initial", "inherit"])(
"applies text-overflow of %s",
(prop) => {
diff --git a/src/components/typography/typography.stories.tsx b/src/components/typography/typography.stories.tsx
index 547df1eeef..4c72abae4c 100644
--- a/src/components/typography/typography.stories.tsx
+++ b/src/components/typography/typography.stories.tsx
@@ -60,6 +60,10 @@ export const VariantsStory: ComponentStory = () => (
The big variant uses larger font-face to draw attention but content has
the same importance as standard text.