Skip to content

Commit d081f73

Browse files
committed
feat(ui-library): forms with&without slot and simple manual validation(#596)
1 parent facbe76 commit d081f73

File tree

16 files changed

+788
-5
lines changed

16 files changed

+788
-5
lines changed

packages/ui-library/src/components/checkbox/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class BlrCheckbox extends LitElement {
4242
@property() label!: string;
4343
@property() checkInputId?: string = '';
4444
@property() arialabel?: string;
45-
45+
@property() required?: boolean;
4646
@property() disabled?: boolean;
4747
@property() checked?: boolean;
4848
@property() indeterminate?: boolean;
@@ -262,10 +262,11 @@ export class BlrCheckbox extends LitElement {
262262
id=${this.checkInputId || nothing}
263263
name=${this.name || nothing}
264264
?disabled=${this.disabled}
265-
?checked=${this.currentCheckedState}
265+
?checked=${this.checked}
266266
?indeterminate=${this.currentIndeterminateState}
267267
?readonly=${this.readonly}
268268
?hasError=${this.hasError}
269+
?required="${this.required}"
269270
@change=${this.handleChange}
270271
aria-hidden="true"
271272
/>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { typeSafeNestedCss as css } from "../../utils/nested-typesafe-css-literals";
2+
import { renderThemedCssStrings } from "../../foundation/_tokens-generated/index.pseudo.generated";
3+
4+
export const { tokenizedLight: captionLight, tokenizedDark: captionDark } = renderThemedCssStrings((componentTokens) => {
5+
const { CaptionComponent } = componentTokens.Forms;
6+
7+
return css`
8+
.blr-form-caption {
9+
width: 100%;
10+
display: flex;
11+
align-items: flex-start;
12+
color: ${CaptionComponent.Text.TextColor.Hint};
13+
14+
&.error {
15+
color: ${CaptionComponent.Text.TextColor.Error};
16+
}
17+
18+
&.sm {
19+
padding: ${CaptionComponent.Container.Padding.SM};
20+
gap: ${CaptionComponent.Container.ItemSpacing.SM};
21+
22+
.blr-icon {
23+
padding-top: ${CaptionComponent.IconWrapper.PaddingTop.SM};
24+
height: ${CaptionComponent.Icon.IconSize.SM};
25+
width: ${CaptionComponent.Icon.IconSize.SM};
26+
}
27+
28+
.blr-caption-text {
29+
padding: ${CaptionComponent.TextWrapper.Padding.SM};
30+
font-family: ${CaptionComponent.Text.Typography.SM.fontFamily}, sans-serif;
31+
font-weight: ${CaptionComponent.Text.Typography.SM.fontWeight};
32+
font-size: ${CaptionComponent.Text.Typography.SM.fontSize};
33+
line-height: ${CaptionComponent.Text.Typography.SM.lineHeight};
34+
}
35+
}
36+
37+
&.md {
38+
padding: ${CaptionComponent.Container.Padding.MD};
39+
gap: ${CaptionComponent.Container.ItemSpacing.MD};
40+
41+
.blr-icon {
42+
padding-top: ${CaptionComponent.IconWrapper.PaddingTop.MD};
43+
height: ${CaptionComponent.Icon.IconSize.MD};
44+
width: ${CaptionComponent.Icon.IconSize.MD};
45+
}
46+
47+
.blr-caption-text {
48+
padding: ${CaptionComponent.TextWrapper.Padding.MD};
49+
font-family: ${CaptionComponent.Text.Typography.MD.fontFamily}, sans-serif;
50+
font-weight: ${CaptionComponent.Text.Typography.MD.fontWeight};
51+
font-size: ${CaptionComponent.Text.Typography.MD.fontSize};
52+
line-height: ${CaptionComponent.Text.Typography.MD.lineHeight};
53+
}
54+
}
55+
56+
&.lg {
57+
padding: ${CaptionComponent.Container.Padding.LG};
58+
gap: ${CaptionComponent.Container.ItemSpacing.LG};
59+
60+
.blr-icon {
61+
padding-top: ${CaptionComponent.IconWrapper.PaddingTop.LG};
62+
height: ${CaptionComponent.Icon.IconSize.LG};
63+
width: ${CaptionComponent.Icon.IconSize.LG};
64+
}
65+
66+
.blr-caption-text {
67+
padding: ${CaptionComponent.TextWrapper.Padding.LG};
68+
font-family: ${CaptionComponent.Text.Typography.LG.fontFamily}, sans-serif;
69+
font-weight: ${CaptionComponent.Text.Typography.LG.fontWeight};
70+
font-size: ${CaptionComponent.Text.Typography.LG.fontSize};
71+
line-height: ${CaptionComponent.Text.Typography.LG.lineHeight};
72+
}
73+
}
74+
}
75+
`;
76+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* eslint-disable no-console */
2+
import { PureIconKeys } from '@boiler/icons';
3+
import { FormSizes, CaptionVariants } from '../../globals/constants';
4+
import { BlrFormExampleWithSlotType } from './index';
5+
import { BlrFormExampleWithSlotRenderFunction } from './renderFunction';
6+
import { Themes } from '../../foundation/_tokens-generated/index.themes';
7+
import { html } from 'lit-html';
8+
import '../../index';
9+
10+
const sharedStyles = html`
11+
<style>
12+
.wrapper {
13+
padding: 1.25em;
14+
background: red;
15+
}
16+
</style>
17+
`;
18+
19+
export default {
20+
title: 'Components/Form Example',
21+
argTypes: {},
22+
parameters: {
23+
badges: ['Draft'],
24+
design: {
25+
type: 'figma',
26+
url: 'https://www.figma.com/file/C4vgEKz8mKyulJ4gm3Qdql/%F0%9F%AB%A7-%5BBLR%5D-The-B01LER?node-id=3618%3A125223&mode=dev',
27+
},
28+
viewMode: 'docs',
29+
layout: 'centered',
30+
docs: {
31+
description: {
32+
component: `<Markdown>
33+
This is experimental form.
34+
</Markdown>
35+
`,
36+
},
37+
},
38+
},
39+
};
40+
41+
export const BlrFormExampleWithSlot = (params: BlrFormExampleWithSlotType) =>
42+
BlrFormExampleWithSlotRenderFunction(params);
43+
44+
BlrFormExampleWithSlot.storyName = 'BlrFormExampleWithSlot';
45+
46+
const args: BlrFormExampleWithSlotType = {
47+
theme: 'Light',
48+
};
49+
50+
BlrFormExampleWithSlot.args = args;
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import { LitElement, html } from 'lit';
2+
import { TAG_NAME } from './renderFunction';
3+
import { ThemeType } from '../../foundation/_tokens-generated/index.themes';
4+
import { property } from 'lit/decorators.js';
5+
6+
export class BlrFormExampleWithSlot extends LitElement {
7+
@property() theme: ThemeType = 'Light';
8+
@property() firstInputValue: string = '';
9+
@property() firstTextInputHasError: boolean = false;
10+
@property() secondInputValue: string = '';
11+
@property() checkBoxChecked: boolean = false;
12+
13+
protected render() {
14+
return html`
15+
<blr-form .handleSubmit="${this.handleSubmit}">
16+
<blr-text-input
17+
size="md"
18+
placeholder="Placeholder-text"
19+
value="${this.firstInputValue}"
20+
maxlength="140"
21+
label="Label-text"
22+
labelappendix="(Appendix)"
23+
arialabel="firstInput"
24+
name="firstInput"
25+
theme="Light"
26+
textinputid="firstInput"
27+
haslabel="true"
28+
type="text"
29+
inputicon="blr360"
30+
showinputicon="true"
31+
@blrTextValueChange="${this.handleFirstInputChange}"
32+
required="true"
33+
errorMessage="input field required"
34+
></blr-text-input>
35+
<blr-text-input
36+
size="md"
37+
placeholder="Placeholder-text"
38+
value="${this.secondInputValue}"
39+
maxlength="140"
40+
label="Label-text"
41+
labelappendix="(Appendix)"
42+
arialabel="secondInput"
43+
name="secondInput"
44+
theme="Light"
45+
textinputid="secondInput"
46+
haslabel="true"
47+
type="text"
48+
inputicon="blr360"
49+
showinputicon="true"
50+
@blrTextValueChange="${this.handleSecondInputChange}"
51+
required="true"
52+
></blr-text-input>
53+
<blr-checkbox
54+
theme="Light"
55+
size="md"
56+
checkedicon="blrCheckmark"
57+
indeterminatedicon="blrMinus"
58+
haslabel="true"
59+
label="I accept the terms and conditions."
60+
erroricon=""
61+
arialabel="check Input"
62+
checkinputid="checkInputId"
63+
name="checkInput"
64+
@blrCheckedChange=${this.handleCheckInput}
65+
errorMessage="must be checked"
66+
required="true"
67+
></blr-checkbox>
68+
</blr-form>
69+
`;
70+
}
71+
72+
private handleSubmit(e) {
73+
const slot = this.renderRoot?.querySelector('slot');
74+
const assignedNodes = slot?.assignedElements({ flatten: true }) ?? [];
75+
assignedNodes.forEach((node: any) => {
76+
if (node.name === 'firstInput') {
77+
const shadowFirstInputElement = node.shadowRoot?.querySelector('input[name="firstInput"]');
78+
if (!shadowFirstInputElement.checkValidity()) {
79+
node._onPropChanged({
80+
detail: {
81+
hasError: true,
82+
errorMessage: 'This is a required field',
83+
},
84+
});
85+
} else {
86+
node._onPropChanged({
87+
detail: {
88+
hasError: false,
89+
errorMessage: '',
90+
},
91+
});
92+
}
93+
}
94+
95+
if (node.name === 'secondInput') {
96+
const shadowSecondInputElement = node.shadowRoot?.querySelector('input[name="secondInput"]');
97+
if (!shadowSecondInputElement.checkValidity()) {
98+
node._onPropChanged({
99+
detail: {
100+
hasError: true,
101+
errorMessage: 'This is a required field',
102+
},
103+
});
104+
} else {
105+
node._onPropChanged({
106+
detail: {
107+
hasError: false,
108+
errorMessage: '',
109+
},
110+
});
111+
}
112+
}
113+
114+
if (node.name === 'checkInput' && !node.hasAttribute('checked')) {
115+
console.error('checkbox has error');
116+
}
117+
});
118+
}
119+
120+
private handleFirstInputChange(evt: any) {
121+
this.firstInputValue = evt.detail.originalEvent.target.value;
122+
}
123+
124+
private handleSecondInputChange(evt: any) {
125+
this.secondInputValue = evt.detail.originalEvent.target.value;
126+
}
127+
128+
private handleCheckInput(evt: any) {
129+
this.checkBoxChecked = evt.detail.checkedState;
130+
}
131+
}
132+
133+
if (!customElements.get(TAG_NAME)) {
134+
customElements.define(TAG_NAME, BlrFormExampleWithSlot);
135+
}
136+
137+
export type BlrFormExampleWithSlotType = Omit<BlrFormExampleWithSlot, keyof LitElement>;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { BlrFormExampleWithSlotType } from '.';
2+
import { genericBlrComponentRenderer } from '../../utils/typesafe-generic-component-renderer';
3+
4+
export const TAG_NAME = 'blr-form-example-with-slot';
5+
6+
export const BlrFormExampleWithSlotRenderFunction = (params: BlrFormExampleWithSlotType) =>
7+
genericBlrComponentRenderer<BlrFormExampleWithSlotType>(TAG_NAME, { ...params });
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { typeSafeNestedCss as css } from "../../utils/nested-typesafe-css-literals";
2+
import { renderThemedCssStrings } from "../../foundation/_tokens-generated/index.pseudo.generated";
3+
4+
export const { tokenizedLight: captionLight, tokenizedDark: captionDark } = renderThemedCssStrings((componentTokens) => {
5+
const { CaptionComponent } = componentTokens.Forms;
6+
7+
return css`
8+
.blr-form-caption {
9+
width: 100%;
10+
display: flex;
11+
align-items: flex-start;
12+
color: ${CaptionComponent.Text.TextColor.Hint};
13+
14+
&.error {
15+
color: ${CaptionComponent.Text.TextColor.Error};
16+
}
17+
18+
&.sm {
19+
padding: ${CaptionComponent.Container.Padding.SM};
20+
gap: ${CaptionComponent.Container.ItemSpacing.SM};
21+
22+
.blr-icon {
23+
padding-top: ${CaptionComponent.IconWrapper.PaddingTop.SM};
24+
height: ${CaptionComponent.Icon.IconSize.SM};
25+
width: ${CaptionComponent.Icon.IconSize.SM};
26+
}
27+
28+
.blr-caption-text {
29+
padding: ${CaptionComponent.TextWrapper.Padding.SM};
30+
font-family: ${CaptionComponent.Text.Typography.SM.fontFamily}, sans-serif;
31+
font-weight: ${CaptionComponent.Text.Typography.SM.fontWeight};
32+
font-size: ${CaptionComponent.Text.Typography.SM.fontSize};
33+
line-height: ${CaptionComponent.Text.Typography.SM.lineHeight};
34+
}
35+
}
36+
37+
&.md {
38+
padding: ${CaptionComponent.Container.Padding.MD};
39+
gap: ${CaptionComponent.Container.ItemSpacing.MD};
40+
41+
.blr-icon {
42+
padding-top: ${CaptionComponent.IconWrapper.PaddingTop.MD};
43+
height: ${CaptionComponent.Icon.IconSize.MD};
44+
width: ${CaptionComponent.Icon.IconSize.MD};
45+
}
46+
47+
.blr-caption-text {
48+
padding: ${CaptionComponent.TextWrapper.Padding.MD};
49+
font-family: ${CaptionComponent.Text.Typography.MD.fontFamily}, sans-serif;
50+
font-weight: ${CaptionComponent.Text.Typography.MD.fontWeight};
51+
font-size: ${CaptionComponent.Text.Typography.MD.fontSize};
52+
line-height: ${CaptionComponent.Text.Typography.MD.lineHeight};
53+
}
54+
}
55+
56+
&.lg {
57+
padding: ${CaptionComponent.Container.Padding.LG};
58+
gap: ${CaptionComponent.Container.ItemSpacing.LG};
59+
60+
.blr-icon {
61+
padding-top: ${CaptionComponent.IconWrapper.PaddingTop.LG};
62+
height: ${CaptionComponent.Icon.IconSize.LG};
63+
width: ${CaptionComponent.Icon.IconSize.LG};
64+
}
65+
66+
.blr-caption-text {
67+
padding: ${CaptionComponent.TextWrapper.Padding.LG};
68+
font-family: ${CaptionComponent.Text.Typography.LG.fontFamily}, sans-serif;
69+
font-weight: ${CaptionComponent.Text.Typography.LG.fontWeight};
70+
font-size: ${CaptionComponent.Text.Typography.LG.fontSize};
71+
line-height: ${CaptionComponent.Text.Typography.LG.lineHeight};
72+
}
73+
}
74+
}
75+
`;
76+
});

0 commit comments

Comments
 (0)