Skip to content

Commit

Permalink
feat(file-upload): implement component
Browse files Browse the repository at this point in the history
  • Loading branch information
dpellier committed Jul 29, 2024
1 parent 317af3d commit 8d4edf6
Show file tree
Hide file tree
Showing 40 changed files with 2,284 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/ods/react/tests/_app/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const componentNames = [
'select',
'modal',
'card',
'file-upload',
//--generator-anchor--
];

Expand Down
23 changes: 23 additions & 0 deletions packages/ods/react/tests/_app/src/components/ods-file-upload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React, { useState } from 'react';
import { type OdsFile, type OdsFileCancelEvent, type OdsFileChangeEvent } from '@ovhcloud/ods-components';
import { OdsFileUpload } from 'ods-components-react';

const FileUpload = () => {
const [files, setFiles] = useState<OdsFile[]>([]);

function onFileCancel(event: OdsFileCancelEvent) {
setFiles(() => files.filter((file) => file.odsId !== event.detail.odsId));
}

function onFileChange(event: OdsFileChangeEvent) {
setFiles(() => files.concat(event.detail));
}

return (
<OdsFileUpload files={ files }
onOdsFileCancel={ onFileCancel }
onOdsFileChange={ onFileChange } />
);
};

export default FileUpload;
23 changes: 23 additions & 0 deletions packages/ods/react/tests/e2e/ods-file-upload.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { Page } from 'puppeteer';
import { goToComponentPage, setupBrowser } from '../setup';

describe('ods-file-upload react', () => {
const setup = setupBrowser();
let page: Page;

beforeAll(async () => {
page = setup().page;
});

beforeEach(async () => {
await goToComponentPage(page, 'ods-file-upload');
});

it('render the component correctly', async () => {
const elem = await page.$('ods-file-upload');
const boundingBox = await elem?.boundingBox();

expect(boundingBox?.height).toBeGreaterThan(0);
expect(boundingBox?.width).toBeGreaterThan(0);
});
});
5 changes: 5 additions & 0 deletions packages/ods/src/components/file-upload/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Local Stencil command generates external ods component build at the root of the project
# Excluding them is a temporary solution to avoid pushing generated files
# But the issue may cause main build (ods-component package) to fails, as it detects multiples occurences
# of the same component and thus you have to delete all those generated dir manually
*/src/
19 changes: 19 additions & 0 deletions packages/ods/src/components/file-upload/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "@ovhcloud/ods-component-file-upload",
"version": "17.1.0",
"private": true,
"description": "ODS FileUpload component",
"main": "dist/index.cjs.js",
"collection": "dist/collection/collection-manifest.json",
"scripts": {
"clean": "rimraf .stencil coverage dist docs-api www",
"doc": "typedoc --pretty --plugin ../../../scripts/typedoc-plugin-decorator.js && node ../../../scripts/generate-typedoc-md.js",
"lint:scss": "stylelint 'src/components/**/*.scss'",
"lint:ts": "eslint '{src,tests}/**/*.{js,ts,tsx}'",
"start": "stencil build --dev --watch --serve",
"test:e2e": "stencil test --e2e --runInBand --config stencil.config.ts",
"test:e2e:ci": "tsc --noEmit && stencil test --e2e --ci --runInBand --config stencil.config.ts",
"test:spec": "stencil test --spec --config stencil.config.ts --coverage",
"test:spec:ci": "tsc --noEmit && stencil test --config stencil.config.ts --spec --ci --coverage"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
@use 'sass:math';
@use '../../../../../style/font';
@use '../../../../../style/input';
@use '../ods-file-upload/ods-file-upload' as file-upload;

:host(.ods-file-upload-item) {
display: grid;
grid-template-columns: max-content 1fr;
column-gap: 8px;
align-items: flex-start;
padding: 0 file-upload.$ods-file-upload-padding;
color: var(--ods-color-text);
}

:host(.ods-file-upload-item--disabled) {
@include input.ods-input-disabled();

.ods-file-upload-item__icon {
color: var(--ods-form-element-foreground-color-disabled);
}
}

.ods-file-upload-item {
&__icon {
margin-top: math.div(file-upload.$ods-file-upload-spacing, 2);
color: var(--ods-color-primary-500);
font-size: font.px-to-rem(24px);
}

&__file {
display: flex;
flex-flow: column;
row-gap: math.div(file-upload.$ods-file-upload-spacing, 2);

&__info {
display: grid;
grid-template-columns: 1fr max-content max-content;
column-gap: file-upload.$ods-file-upload-spacing;
align-items: baseline;

&__name {
@include font.ellipsis();
}

&__size {
font-size: font.px-to-rem(12px);
}
}

&__error {
color: var(--ods-color-critical-500);
font-size: font.px-to-rem(12px);
font-weight: 600;
}

&__progress {
display: grid;
grid-template-columns: 1fr max-content;
column-gap: file-upload.$ods-file-upload-spacing;
align-items: center;
font-size: font.px-to-rem(14px);
font-weight: 600;

&__bar {
display: flex;

&::part(progress) {
width: 100%;
}
}
}

&__success {
color: var(--ods-color-success-500);
font-size: font.px-to-rem(12px);
font-weight: 600;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Component, type FunctionalComponent, Host, Prop, h } from '@stencil/core';
import { formatBytes } from '../../../../../utils/file';
import { ODS_BUTTON_SIZE, ODS_BUTTON_VARIANT } from '../../../../button/src';
import { ODS_ICON_NAME } from '../../../../icon/src';

@Component({
shadow: true,
styleUrl: 'ods-file-upload-item.scss',
tag: 'ods-file-upload-item',
})
export class OdsFileUploadItem {
@Prop({ reflect: false }) public cancelHandler!: () => void;
@Prop({ reflect: true }) public error: string = '';
@Prop({ reflect: true }) public isDisabled: boolean = false;
@Prop({ reflect: true }) public isUploaded: boolean = false;
@Prop({ reflect: true }) public name!: string;
@Prop({ reflect: true }) public progress?: number;
@Prop({ reflect: true }) public size!: number;
@Prop({ reflect: true }) public successLabel!: string;

private renderStatus(): FunctionalComponent {
if (this.error) {
return (
<span class="ods-file-upload-item__file__error">
{ this.error }
</span>
);
}

if (this.progress && this.progress < 100) {
return (
<div class="ods-file-upload-item__file__progress">
<ods-progress-bar
class="ods-file-upload-item__file__progress__bar"
max={ 100 }
value={ this.progress }>
</ods-progress-bar>

<span>
{ this.progress }%
</span>
</div>
);
}

if (this.isUploaded) {
return (
<span class="ods-file-upload-item__file__success">
<ods-icon name={ ODS_ICON_NAME.check }></ods-icon>&nbsp;{ this.successLabel }
</span>
);
}

return <span></span>;
}

render(): FunctionalComponent {
return (
<Host
class={{
'ods-file-upload-item': true,
'ods-file-upload-item--disabled': this.isDisabled,
}}>
<ods-icon
class="ods-file-upload-item__icon"
name={ ODS_ICON_NAME.file }>
</ods-icon>

<div class="ods-file-upload-item__file">
<div class="ods-file-upload-item__file__info">
<span class="ods-file-upload-item__file__info__name">
{ this.name }
</span>

<span class="ods-file-upload-item__file__info__size">
{ formatBytes(this.size) }
</span>

<ods-button
icon={ ODS_ICON_NAME.cross }
isDisabled={ this.isDisabled }
label=""
onClick={ () => this.cancelHandler() }
size={ ODS_BUTTON_SIZE.sm }
variant={ ODS_BUTTON_VARIANT.ghost }>
</ods-button>
</div>

{ this.renderStatus() }
</div>
</Host>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
@use 'sass:math';
@use '../../../../../style/font';
@use '../../../../../style/input';

$ods-file-upload-padding: 24px;
$ods-file-upload-spacing: 8px;

:host(.ods-file-upload) {
display: inline-flex;
flex-flow: column;
row-gap: 8px;
border: dashed 1px var(--ods-form-element-border-color-default);
border-radius: 4px;
overflow: hidden;
color: var(--ods-color-text);
}

:host(.ods-file-upload--disabled) {
@include input.ods-input-disabled();

.ods-file-upload__dropzone__icon {
color: var(--ods-form-element-foreground-color-disabled);
}
}

.ods-file-upload {
&__dropzone {
display: inline-flex;
flex-flow: column;
row-gap: $ods-file-upload-spacing;
align-items: center;
padding: $ods-file-upload-padding $ods-file-upload-padding 0;

&--dragging {
background-color: var(--ods-color-primary-100);
}

&--empty {
padding: $ods-file-upload-padding;
}

&__input {
display: none;
}

&__icon {
color: var(--ods-color-primary-500);
font-size: font.px-to-rem(32px);
}

&__file-format {
font-size: font.px-to-rem(12px);
}

&__error {
color: var(--ods-color-critical-500);
font-size: font.px-to-rem(12px);
font-weight: 600;
}
}

&__list {
display: flex;
flex-flow: column;
row-gap: math.div($ods-file-upload-spacing, 2);
padding-bottom: $ods-file-upload-padding;
}
}
Loading

0 comments on commit 8d4edf6

Please sign in to comment.