Skip to content

Commit

Permalink
feat(FileUploader): add drag and drop file uploader (#3872)
Browse files Browse the repository at this point in the history
  • Loading branch information
emyarod authored and joshblack committed Sep 4, 2019
1 parent 34d1fdd commit 8a4fae7
Show file tree
Hide file tree
Showing 11 changed files with 975 additions and 88 deletions.
185 changes: 156 additions & 29 deletions packages/components/src/components/file-uploader/_file-uploader.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
.#{$prefix}--file--label {
@include reset;
@include type-style('heading-01');

color: $text-01;
margin-bottom: $carbon--spacing-03;
}
Expand All @@ -40,12 +39,60 @@
@include hidden;
}

/// This class is of old markup with "select file" button
/// New code should use link-style "select file" UI (`.bx--file-browse-btn`)
/// @deprecated

This comment has been minimized.

Copy link
@mattrosno

mattrosno Sep 5, 2019

Member

@emyarod I know this was merged, but can you update these to be double forward slashes so SassDoc doesn't pick them up?

See very last entry here. .#{$prefix}--file-btn should not be in our SassDoc. https://github.com/carbon-design-system/carbon/blob/master/packages/components/docs/sass.md#general

https://github.com/carbon-design-system/carbon/blob/master/.github/CONTRIBUTING.md#sass-documentation

Our contribution guidelines state how /// should not be used for non-SassDoc comments.

This comment has been minimized.

Copy link
@emyarod

emyarod Sep 5, 2019

Author Member

@mattrosno I think this was introduced in https://github.com/carbon-design-system/carbon/pull/2848/files#diff-5f3ec9060782b3ceac52ba329091e7a4R43-R45, which was combined with this PR

do you want to remove all of the other triple slash @deprecated comments too? or should we just add /// @access private?

This comment has been minimized.

Copy link
@mattrosno

mattrosno Sep 5, 2019

Member

@emyarod thanks for cleaning it up. We really only annotate Sass functions, mixins, and variables for SassDoc with ///, not classname declaration blocks. I'd convert those three lines to use // so SassDoc doesn't pick it up, and denote "deprecated" in a non-structured way. Some examples looking through our source:

// For deprecated older markup
&.#{$prefix}--inline-loading__svg {
...

// ::before - Tooltip caret
// ::after - Legacy Tooltip content (deprecated)
&::before,
...

This comment has been minimized.

Copy link
@emyarod

emyarod Sep 5, 2019

Author Member

@mattrosno got it, should I change the other triple slash @deprecated comments too? for example in _tooltip.scss and a few of our global sass files

This comment has been minimized.

Copy link
@mattrosno

mattrosno Sep 5, 2019

Member

@emyarod /// @deprecated comments are fine if they annotate a function, mixin or variable. SassDoc parses that tag and then in our generated markdown, we denote it with the ⚠️. https://github.com/carbon-design-system/carbon/blob/master/packages/components/docs/sass.md

This comment has been minimized.

Copy link
@emyarod

emyarod Sep 5, 2019

Author Member
.#{$prefix}--file-btn {
display: inline-flex;
margin: 0;
padding-right: rem(64px);
}

.#{$prefix}--file-browse-btn {
margin-bottom: $carbon--spacing-03;
display: inline-block;
width: 100%;
color: $link-01;
outline: none;
transition: $duration--fast-02 motion(standard, productive);
cursor: pointer;
outline: 2px solid transparent;
outline-offset: -2px;

&:focus,
&:hover {
outline: 2px solid $interactive-03;
}

&:hover,
&:focus,
&:active,
&:active:visited {
text-decoration: underline;
}

&:active {
color: $text-01;
}
}

.#{$prefix}--file-browse-btn--disabled {
cursor: no-drop;
text-decoration: none;
color: $disabled-02;

&:hover,
&:focus {
outline: none;
text-decoration: none;
color: $disabled-02;
}
}

.#{$prefix}--file-browse-btn--disabled .#{$prefix}--file__drop-container {
border: 1px dashed $disabled-01;
}

.#{$prefix}--label-description {
@include reset;
@include type-style('body-short-01');
Expand All @@ -54,85 +101,165 @@
margin-bottom: $carbon--spacing-05;
}

.#{$prefix}--file-container {
display: block;
width: 100%;
.#{$prefix}--file-btn ~ .#{$prefix}--file-container {
margin-top: $carbon--spacing-06;
}

.#{$prefix}--file__selected-file {
display: flex;
display: grid;
grid-gap: $carbon--spacing-03 $carbon--spacing-05;
grid-template-columns: 1fr auto;
grid-auto-rows: auto;
align-items: center;
justify-content: space-between;
height: rem(40px);
max-width: rem(300px);
min-height: $carbon--spacing-07;
max-width: rem(320px);
margin-bottom: $carbon--spacing-03;
padding: 0 $carbon--spacing-03 0 $carbon--spacing-05;
background-color: $field-01;
overflow: hidden;
word-break: break-word;

&:last-child {
margin-bottom: 0;
}

.#{$prefix}--inline-loading__animation,
.#{$prefix}--form-requirement {
grid-column-start: 1;
grid-column-end: -1;
max-height: none;
margin: 0;
}

.#{$prefix}--loading {
right: -0.25rem; // offset for loading svg container
width: 1.5rem;
height: 1.5rem;
width: $carbon--spacing-06;
height: $carbon--spacing-06;
}

.#{$prefix}--file-filename {
@include type-style('body-short-01');
margin-left: $carbon--spacing-05;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}

// TODO: deprecate this block
.#{$prefix}--file__selected-file--invalid__wrapper {
@include focus-outline('invalid');
outline-width: 1px;
background-color: $field-01;
max-width: rem(320px);
margin-bottom: $carbon--spacing-03;
}

.#{$prefix}--file__selected-file--invalid {
@include focus-outline('invalid');
margin-bottom: $carbon--spacing-02;
outline-width: 1px;
padding: $carbon--spacing-04 0;
}

.#{$prefix}--file__selected-file--invalid .#{$prefix}--form-requirement {
border-top: 1px solid $ui-03;
padding-top: $carbon--spacing-03;
}

.#{$prefix}--file__selected-file--invalid
.#{$prefix}--form-requirement__title,
.#{$prefix}--file__selected-file--invalid
.#{$prefix}--form-requirement__supplement {
@include type-style('label-01');
padding-right: $carbon--spacing-03;
padding-left: $carbon--spacing-05;
}

.#{$prefix}--file__selected-file--invalid
.#{$prefix}--form-requirement__supplement {
color: $text-01;
}

// TODO: deprecate
.#{$prefix}--file__selected-file--invalid + .#{$prefix}--form-requirement {
@include type-style('caption-01');
display: block;
max-height: rem(200px);
color: $support-01;
font-weight: 400;
margin: 0 0 $carbon--spacing-03 0;
padding: $carbon--spacing-03 $carbon--spacing-05;
overflow: visible;
}

.#{$prefix}--file-filename {
@include type-style('body-short-01');
@include text-overflow(300px);
display: inline-block;
align-items: center;
.#{$prefix}--file__selected-file--invalid
+ .#{$prefix}--form-requirement
.#{$prefix}--form-requirement__supplement {
padding-bottom: $carbon--spacing-03;
color: $text-01;
margin-right: $carbon--spacing-05;
padding: 1px 0;
/*rtl:ignore*/
direction: ltr;
justify-content: flex-start; /*rtl:{flex-end}*/
}

.#{$prefix}--file__state-container {
display: flex;
align-items: center;
justify-content: center;
min-width: 1.5rem;
padding-right: $carbon--spacing-03;

.#{$prefix}--loading__svg {
stroke: $ui-05;
}
}

.#{$prefix}--file__state-container .#{$prefix}--file-complete {
fill: $support-02;
fill: $interactive-04;
cursor: pointer;

&:focus {
@include focus-outline('border');
}

// for checkmark contrast
[data-icon-path='inner-path'] {
opacity: 1;
fill: $icon-03;
}
}

.#{$prefix}--file__state-container .#{$prefix}--file-invalid {
height: $carbon--spacing-05;
width: $carbon--spacing-05;
fill: $support-01;
margin-right: $carbon--spacing-03;
}

.#{$prefix}--file__state-container .#{$prefix}--file-close {
background: transparent;
height: $carbon--spacing-05;
width: $carbon--spacing-05;
background-color: transparent;
border: none;
cursor: pointer;
padding: 0;
fill: $icon-02;

&:focus {
@include focus-outline('border');
}
}

.#{$prefix}--file__state-container .#{$prefix}--file-close svg path {
fill: $icon-02;
}

.#{$prefix}--file__drop-container {
display: flex;
align-items: flex-start;
justify-content: space-between;
height: rem(96px);
max-width: rem(320px);
padding: $carbon--spacing-05;
overflow: hidden;
border: 1px dashed $ui-04;
}

.#{$prefix}--file__drop-container--drag-over {
background: none;
outline: 2px solid $interactive-03;
outline-offset: -2px;
}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/react/src/__tests__/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ describe('Carbon Components React', () => {
"ExpandableTile",
"FileUploader",
"FileUploaderButton",
"FileUploaderDropContainer",
"FileUploaderItem",
"FileUploaderSkeleton",
"Filename",
"Form",
Expand Down
70 changes: 68 additions & 2 deletions packages/react/src/components/FileUploader/FileUploader-story.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';

import {
withKnobs,
array,
Expand All @@ -19,10 +18,12 @@ import {
select,
text,
} from '@storybook/addon-knobs';
import { settings } from 'carbon-components';
import FileUploader, { FileUploaderButton } from '../FileUploader';
import FileUploaderSkeleton from '../FileUploader/FileUploader.Skeleton';
import Button from '../Button';
import { settings } from 'carbon-components';
import FileUploaderItem from './FileUploaderItem';
import FileUploaderDropContainer from './FileUploaderDropContainer';

const { prefix } = settings;
const buttonKinds = {
Expand Down Expand Up @@ -75,6 +76,38 @@ const props = {
name: text('Form item name: (name)', ''),
multiple: boolean('Supports multiple files (multiple)', true),
}),
fileUploaderItem: () => ({
name: text('Filename (name)', 'README.md'),
status: select('Status for file name (status)', filenameStatuses, 'edit'),
iconDescription: text('(iconDescription)', 'Icon description'),
onDelete: action('onDelete'),
invalid: boolean('Invalid (invalid)', false),
errorSubject: text(
'Error subject (errorSubject)',
'File size exceeds limit'
),
errorBody: text(
'Error body (errorBody)',
'500kb max file size. Select a new file and try again.'
),
}),
fileUploaderDropContainer: () => ({
labelText: text(
'Label text (labelText)',
'Drag and drop files here or click to upload'
),
name: text('Form item name (name)', ''),
multiple: boolean('Supports multiple files (multiple)', true),
accept: array(
'Accepted MIME types or file extensions (accept)',
['image/jpeg', 'image/png'],
','
),
disabled: boolean('Disabled (disabled)', false),
role: text('ARIA role of the button (role)', ''),
tabIndex: number('Tab index (tabIndex)', 0),
onChange: action('onChange'),
}),
};

storiesOf('FileUploader', module)
Expand Down Expand Up @@ -120,6 +153,39 @@ storiesOf('FileUploader', module)
},
}
)
.add(
'FileUploaderItem',
() => <FileUploaderItem {...props.fileUploaderItem()} />,
{
info: {
text: `
<FileUploaderItem /> represents an item that has been uploaded to the file uploader component. Use the \`status\` prop to control which icon appears ('edit', 'complete', or 'uploading').
`,
},
}
)
.add(
'FileUploaderDropContainer',
() => <FileUploaderDropContainer {...props.fileUploaderDropContainer()} />,
{
info: {
text:
'<FileUploaderDropContainer /> is a drag and drop file uploader which allows users to upload files via both the normal file selection dialog and by dragging and dropping files.',
},
}
)
.add(
'Drag and drop upload container example application',
() =>
require('./stories/drop-container').default(
props.fileUploaderDropContainer()
),
{
info: {
text: 'Example application with drag and drop file uploader',
},
}
)
.add(
'skeleton',
() => (
Expand Down
Loading

0 comments on commit 8a4fae7

Please sign in to comment.