Skip to content

Commit

Permalink
feat(file-input): add file input component (backport to 16.x) (#1414)
Browse files Browse the repository at this point in the history
This is a port of 7a61bf4 (#1373) to 16.x.

This commit introduces the file input/picker component.

The previous undocumented `.clr-file-wrapper` styles are now deprecated
due to design changes. The component uses separate
`.clr-file-input-wrapper` styles, and these styles supersede the
deprecated styles for CSS-only implementations.

CDE-1668
  • Loading branch information
kevinbuhmann authored May 30, 2024
1 parent 40f47e0 commit 4d7f0d9
Show file tree
Hide file tree
Showing 73 changed files with 1,595 additions and 120 deletions.
139 changes: 139 additions & 0 deletions .storybook/stories/file-input/file-input-states.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright (c) 2016-2024 Broadcom. All Rights Reserved.
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/

import { FormsModule } from '@angular/forms';
import { ClrFileInputModule, ClrFormLayout } from '@clr/angular';
import { moduleMetadata, StoryContext, StoryFn, StoryObj } from '@storybook/angular';

import { clearFiles, selectFiles } from '../../../projects/angular/src/forms/file-input/file-input.helpers';

export default {
title: 'File Input/File Input States',
decorators: [
moduleMetadata({
imports: [FormsModule, ClrFileInputModule],
}),
],
argTypes: {
// form inputs
clrLayout: { control: false },
},
};

const fileInputStatesTemplate: StoryFn = args => ({
template: `
<form clrForm [clrLayout]="clrLayout">
<clr-file-input-container>
<label>Success State</label>
<input
id="success-state-file-input"
type="file"
name="success-state-file"
[(ngModel)]="successStateFile"
clrFileInput
required
multiple
/>
<clr-control-helper>Helper message</clr-control-helper>
<clr-control-success>Success message</clr-control-success>
<clr-control-error *clrIfError="'required'">Required</clr-control-error>
</clr-file-input-container>
<clr-file-input-container>
<label>Error State</label>
<input
id="error-state-file-input"
type="file"
name="error-state-file"
[(ngModel)]="errorStateFile"
clrFileInput
required
multiple
/>
<clr-control-helper>Helper message</clr-control-helper>
<clr-control-error *clrIfError="'required'">Required</clr-control-error>
</clr-file-input-container>
<clr-file-input-container>
<label>Multiple Files</label>
<input
id="multiple-files-file-input"
type="file"
name="multiple-files-file"
[(ngModel)]="multipleFilesFile"
clrFileInput
required
multiple
/>
<clr-control-helper>Helper message</clr-control-helper>
<clr-control-success>Success message</clr-control-success>
<clr-control-error *clrIfError="'required'">Required</clr-control-error>
</clr-file-input-container>
<clr-file-input-container>
<label>Long Filename</label>
<input
id="long-filename-file-input"
type="file"
name="long-filename-file"
[(ngModel)]="longFilenameFile"
clrFileInput
required
multiple
/>
<clr-control-helper>Helper message</clr-control-helper>
<clr-control-success>Success message</clr-control-success>
<clr-control-error *clrIfError="'required'">Required</clr-control-error>
</clr-file-input-container>
<clr-file-input-container>
<label>Disabled</label>
<input type="file" clrFileInput disabled />
</clr-file-input-container>
</form>
`,
props: { ...args },
});

export const VerticalFileInputStates: StoryObj = {
render: fileInputStatesTemplate,
play: fileInputStatesPlayFn,
args: {
clrLayout: ClrFormLayout.VERTICAL,
},
};

export const HorizontalFileInputStates: StoryObj = {
render: fileInputStatesTemplate,
play: fileInputStatesPlayFn,
args: {
clrLayout: ClrFormLayout.HORIZONTAL,
},
};

export const CompactFileInputStates: StoryObj = {
render: fileInputStatesTemplate,
play: fileInputStatesPlayFn,
args: {
clrLayout: ClrFormLayout.COMPACT,
},
};

function fileInputStatesPlayFn({ canvasElement }: StoryContext) {
const successStateFileInputElement = canvasElement.querySelector<HTMLInputElement>('#success-state-file-input');
selectFiles(successStateFileInputElement, [new File([''], 'file.txt')]);

const errorStateFileInputElement = canvasElement.querySelector<HTMLInputElement>('#error-state-file-input');
selectFiles(errorStateFileInputElement, [new File([''], 'file.txt')]);
clearFiles(errorStateFileInputElement);

const multipleFilesFileInputElement = canvasElement.querySelector<HTMLInputElement>('#multiple-files-file-input');
selectFiles(multipleFilesFileInputElement, [new File([''], 'file-1.txt'), new File([''], 'file-2.txt')]);

const longFilenameFileInputElement = canvasElement.querySelector<HTMLInputElement>('#long-filename-file-input');
selectFiles(longFilenameFileInputElement, [new File([''], 'long-filename-lorem-ipsum-dolor-sit-amet.txt')]);
}
65 changes: 65 additions & 0 deletions .storybook/stories/file-input/file-input.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2016-2024 Broadcom. All Rights Reserved.
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
* This software is released under MIT license.
* The full license information can be found in LICENSE in the root directory of this project.
*/

import { FormsModule } from '@angular/forms';
import { ClrFileInputModule, ClrFormLayout, commonStringsDefault } from '@clr/angular';
import { moduleMetadata, StoryFn, StoryObj } from '@storybook/angular';

export default {
title: 'File Input/File Input',
decorators: [
moduleMetadata({
imports: [FormsModule, ClrFileInputModule],
}),
],
argTypes: {
// inputs
clrButtonLabel: { type: 'string' },
// form inputs
clrLayout: { control: false },
},
args: {
// inputs
clrButtonLabel: commonStringsDefault.browse,
},
};

const fileInputTemplate: StoryFn = args => ({
template: `
<form clrForm [clrLayout]="clrLayout">
<clr-file-input-container [clrButtonLabel]="clrButtonLabel">
<label>File</label>
<input type="file" name="file" [(ngModel)]="file" clrFileInput required multiple />
<clr-control-helper>Helper message</clr-control-helper>
<clr-control-success>Success message</clr-control-success>
<clr-control-error *clrIfError="'required'">Required</clr-control-error>
</clr-file-input-container>
</form>
`,
props: { ...args },
});

export const VerticalFileInput: StoryObj = {
render: fileInputTemplate,
args: {
clrLayout: ClrFormLayout.VERTICAL,
},
};

export const HorizontalFileInput: StoryObj = {
render: fileInputTemplate,
args: {
clrLayout: ClrFormLayout.HORIZONTAL,
},
};

export const CompactFileInput: StoryObj = {
render: fileInputTemplate,
args: {
clrLayout: ClrFormLayout.COMPACT,
},
};
Loading

0 comments on commit 4d7f0d9

Please sign in to comment.