Skip to content

Commit

Permalink
feat(html): add otp component spec
Browse files Browse the repository at this point in the history
  • Loading branch information
zhpenkov committed Dec 6, 2024
1 parent 40b704c commit 566aa60
Show file tree
Hide file tree
Showing 10 changed files with 413 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/html/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export * from './searchbox/index';
export * from './switch/index';
export * from './upload/index';
export * from './dropzone/index';
export * from './otp/index';

// Command interfaces
export * from './fab/index';
Expand Down
4 changes: 4 additions & 0 deletions packages/html/src/otp/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './otp.spec';
export * from './otp-input';
export * from './otp-separator';
export * from './templates/otp-normal';
101 changes: 101 additions & 0 deletions packages/html/src/otp/otp-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { classNames, States, Size, Roundness, FillMode } from '../misc';
import { Input, InputInnerInput, InputLoadingIcon } from '../input';

export const ONETIMEPASSWORDINPUT_CLASSNAME = `k-otp-input`;

const states = [
States.hover,
States.focus,
States.valid,
States.invalid,
States.required,
States.disabled,
States.loading,
States.readonly
];

const options = {
size: [Size.small, Size.medium, Size.large],
rounded: [Roundness.small, Roundness.medium, Roundness.large, Roundness.full],
fillMode: [FillMode.solid, FillMode.flat, FillMode.outline]
};

export type KendoOneTimePasswordInputOptions = {
size?: (typeof options.size)[number] | null;
rounded?: (typeof options.rounded)[number] | null;
fillMode?: (typeof options.fillMode)[number] | null;
};

export type KendoOneTimePasswordInputProps = KendoOneTimePasswordInputOptions & {
type?: string;
value?: string;
placeholder?: string;
autocomplete?: string;
};

export type KendoOneTimePasswordInputState = { [K in (typeof states)[number]]?: boolean };

const defaultOptions = {
size: Input.defaultOptions.size,
rounded: Input.defaultOptions.rounded,
fillMode: Input.defaultOptions.fillMode,
};

export const OneTimePasswordInput = (
props: KendoOneTimePasswordInputProps &
KendoOneTimePasswordInputState &
Omit<React.HTMLAttributes<HTMLSpanElement>, 'prefix'>
) => {
const {
size = defaultOptions.size,
rounded = defaultOptions.rounded,
fillMode = defaultOptions.fillMode,
type,
value,
placeholder,
hover,
focus,
valid,
invalid,
required,
loading,
disabled,
readonly,
...other
} = props;


return (
<Input
{...other}
size={size}
rounded={rounded}
fillMode={fillMode}
hover={hover}
focus={focus}
valid={valid}
invalid={invalid}
required={required}
loading={loading}
disabled={disabled}
readonly={readonly}
className={classNames(props.className,
ONETIMEPASSWORDINPUT_CLASSNAME
)}
>
{loading ||
<InputInnerInput placeholder={placeholder} value={value} type={type} />
}
<InputLoadingIcon
loading={loading}
disabled={disabled} />
</Input>
);
};

OneTimePasswordInput.states = states;
OneTimePasswordInput.options = options;
OneTimePasswordInput.className = ONETIMEPASSWORDINPUT_CLASSNAME;
OneTimePasswordInput.defaultOptions = defaultOptions;

export default OneTimePasswordInput;
38 changes: 38 additions & 0 deletions packages/html/src/otp/otp-separator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { classNames } from '../misc';

const ONETIMEPASSWORDSEPARATOR_CLASSNAME = `k-otp-separator`;

const states = [];

const options = {};

const defaultOptions = {};

export const OneTimePasswordSeparator = (props:
React.HTMLAttributes<HTMLDivElement>
) => {
const {
children,
...other
} = props;


return (
<div
{...other}
className={classNames(
props.className,
ONETIMEPASSWORDSEPARATOR_CLASSNAME
)}
>
{children}
</div>
);
};

OneTimePasswordSeparator.states = states;
OneTimePasswordSeparator.options = options;
OneTimePasswordSeparator.className = ONETIMEPASSWORDSEPARATOR_CLASSNAME;
OneTimePasswordSeparator.defaultOptions = defaultOptions;

export default OneTimePasswordSeparator;
62 changes: 62 additions & 0 deletions packages/html/src/otp/otp.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { classNames, optionClassNames, stateClassNames, States, FillMode } from '../misc';

export const ONETIMEPASSWORD_CLASSNAME = `k-otp`;

const states = [
States.disabled
];

const options = {
fillMode: [FillMode.solid, FillMode.flat, FillMode.outline, FillMode.clear, FillMode.link],
};

export type KendoOneTimePasswordOptions = {
fillMode?: (typeof options.fillMode)[number] | null;
};

export type KendoOneTimePasswordProps = KendoOneTimePasswordOptions & {
// stretched?: boolean;
};

export type KendoOneTimePasswordState = { [K in (typeof states)[number]]?: boolean };

const defaultOptions = {
fillMode: FillMode.solid
};

export const OneTimePassword = (
props: KendoOneTimePasswordProps &
KendoOneTimePasswordState &
React.HTMLAttributes<HTMLDivElement>
) => {
const {
fillMode = defaultOptions.fillMode,
disabled,
...other
} = props;

return (
<div
{...other}
className={classNames(
props.className,
ONETIMEPASSWORD_CLASSNAME,
optionClassNames(ONETIMEPASSWORD_CLASSNAME, {
fillMode
}),
stateClassNames(ONETIMEPASSWORD_CLASSNAME, {
disabled
})
)}
>
{props.children}
</div>
);
};

OneTimePassword.states = states;
OneTimePassword.options = options;
OneTimePassword.className = ONETIMEPASSWORD_CLASSNAME;
OneTimePassword.defaultOptions = defaultOptions;

export default OneTimePassword;
15 changes: 15 additions & 0 deletions packages/html/src/otp/templates/otp-normal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { OneTimePassword, OneTimePasswordInput, OneTimePasswordSeparator } from "..";

export const OneTimePasswordNormal = (props) => (
<OneTimePassword children={
<>
<OneTimePasswordInput {...props}></OneTimePasswordInput>
<OneTimePasswordInput {...props}></OneTimePasswordInput>
<OneTimePasswordInput {...props}></OneTimePasswordInput>
<OneTimePasswordSeparator>-</OneTimePasswordSeparator>
<OneTimePasswordInput {...props}></OneTimePasswordInput>
<OneTimePasswordInput {...props}></OneTimePasswordInput>
<OneTimePasswordInput {...props}></OneTimePasswordInput>
</>
} />
);
52 changes: 52 additions & 0 deletions packages/html/src/otp/tests/otp-flat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { OneTimePasswordNormal } from '..';
import { Input } from '../../input';

const styles = `
#test-area {
max-width: 660px;
}
`;

export default () =>(
<>
<style>{styles}</style>
<div id="test-area" className="k-d-grid k-grid-cols-2">

<span>TextBox</span>
<span>TextBox RTL</span>

<div>
<OneTimePasswordNormal fillMode="flat" />
</div>
<div dir="rtl">
<OneTimePasswordNormal fillMode="flat" />
</div>

<div>
<OneTimePasswordNormal fillMode="flat" placeholder="#" />
</div>
<div dir="rtl">
<OneTimePasswordNormal fillMode="flat" placeholder="#" />
</div>

{[ 'normal', ...Input.states ].map((state) => (
<>
<div>
<OneTimePasswordNormal fillMode="flat" { ...{ [state]: true }} value="0" />
</div>

<div dir="rtl">
<OneTimePasswordNormal fillMode="flat" { ...{ [state]: true }} value="0" />
</div>
</>
))}

<div>
<OneTimePasswordNormal fillMode="flat" value="0" invalid focus />
</div>
<div dir="rtl">
<OneTimePasswordNormal fillMode="flat" value="0" invalid focus />
</div>
</div>
</>
);
52 changes: 52 additions & 0 deletions packages/html/src/otp/tests/otp-outline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { OneTimePasswordNormal } from '..';
import { Input } from '../../input';

const styles = `
#test-area {
max-width: 660px;
}
`;

export default () =>(
<>
<style>{styles}</style>
<div id="test-area" className="k-d-grid k-grid-cols-2">

<span>TextBox</span>
<span>TextBox RTL</span>

<div>
<OneTimePasswordNormal fillMode="outline" />
</div>
<div dir="rtl">
<OneTimePasswordNormal fillMode="outline" />
</div>

<div>
<OneTimePasswordNormal fillMode="outline" placeholder="#" />
</div>
<div dir="rtl">
<OneTimePasswordNormal fillMode="outline" placeholder="#" />
</div>

{[ 'normal', ...Input.states ].map((state) => (
<>
<div>
<OneTimePasswordNormal fillMode="outline" { ...{ [state]: true }} value="0" />
</div>

<div dir="rtl">
<OneTimePasswordNormal fillMode="outline" { ...{ [state]: true }} value="0" />
</div>
</>
))}

<div>
<OneTimePasswordNormal fillMode="outline" value="0" invalid focus />
</div>
<div dir="rtl">
<OneTimePasswordNormal fillMode="outline" value="0" invalid focus />
</div>
</div>
</>
);
36 changes: 36 additions & 0 deletions packages/html/src/otp/tests/otp-size-rounded.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { OneTimePasswordNormal } from '..';
import { Input } from '../../input';

const styles = `
#test-area {
max-width: 1240px;
grid-template-columns: 80px 320px 320px 1fr;
}
`;

export default () =>(
<>
<style>{styles}</style>
<div id="test-area" className="k-d-grid">
<span>
<small>rounded / size</small>
</span>
<span>small</span>
<span>medium</span>
<span>large</span>

{ Input.options.rounded.map((rounded) => (
<>
<span>{ rounded }</span>
{ Input.options.size.map((size) => (
<>
<div>
<OneTimePasswordNormal size={size} rounded={rounded} value="0"/>
</div>
</>
))}
</>
))}
</div>
</>
);
Loading

0 comments on commit 566aa60

Please sign in to comment.