Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
AmendsenProject
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Shaik Janipasha
AmendsenProject
Commits
b2230452
Unverified
Commit
b2230452
authored
Sep 02, 2020
by
Marcos Iglesias
Committed by
GitHub
Sep 02, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: Empty and Loading states for the table (#639)
Signed-off-by:
Marcos Iglesias Valle
<
golodhros@gmail.com
>
parent
97a5e14f
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
357 additions
and
31 deletions
+357
-31
_animations.scss
amundsen_application/static/css/_animations.scss
+5
-1
index.spec.tsx
...lication/static/js/components/common/Table/index.spec.tsx
+208
-0
index.tsx
...n_application/static/js/components/common/Table/index.tsx
+96
-30
styles.scss
...application/static/js/components/common/Table/styles.scss
+33
-0
table.story.tsx
...ication/static/js/components/common/Table/table.story.tsx
+6
-0
testDataBuilder.ts
...tion/static/js/components/common/Table/testDataBuilder.ts
+9
-0
No files found.
amundsen_application/static/css/_animations.scss
View file @
b2230452
...
@@ -16,7 +16,7 @@ $loading-curve: cubic-bezier(0.45, 0, 0.15, 1);
...
@@ -16,7 +16,7 @@ $loading-curve: cubic-bezier(0.45, 0, 0.15, 1);
}
}
}
}
.
is-shimmer-animated
{
%
is-shimmer-animated
{
animation
:
$loading-duration
shimmer
$loading-curve
infinite
;
animation
:
$loading-duration
shimmer
$loading-curve
infinite
;
background-image
:
linear-gradient
(
background-image
:
linear-gradient
(
to
right
,
to
right
,
...
@@ -29,3 +29,7 @@ $loading-curve: cubic-bezier(0.45, 0, 0.15, 1);
...
@@ -29,3 +29,7 @@ $loading-curve: cubic-bezier(0.45, 0, 0.15, 1);
background-repeat
:
no-repeat
;
background-repeat
:
no-repeat
;
background-size
:
300%
100%
;
background-size
:
300%
100%
;
}
}
.is-shimmer-animated
{
@extend
%is-shimmer-animated
;
}
amundsen_application/static/js/components/common/Table/index.spec.tsx
View file @
b2230452
...
@@ -30,6 +30,83 @@ describe('Table', () => {
...
@@ -30,6 +30,83 @@ describe('Table', () => {
}).
not
.
toThrow
();
}).
not
.
toThrow
();
});
});
describe
(
'when empty data is passed'
,
()
=>
{
const
{
columns
,
data
}
=
dataBuilder
.
withEmptyData
().
build
();
it
(
'renders a table'
,
()
=>
{
const
{
wrapper
}
=
setup
({
data
,
columns
,
});
const
expected
=
1
;
const
actual
=
wrapper
.
find
(
'.ams-table'
).
length
;
expect
(
actual
).
toEqual
(
expected
);
});
describe
(
'table header'
,
()
=>
{
it
(
'renders a table header'
,
()
=>
{
const
{
wrapper
}
=
setup
({
data
,
columns
,
});
const
expected
=
1
;
const
actual
=
wrapper
.
find
(
'.ams-table-header'
).
length
;
expect
(
actual
).
toEqual
(
expected
);
});
it
(
'renders one cell inside the header'
,
()
=>
{
const
{
wrapper
}
=
setup
({
data
,
columns
,
});
const
expected
=
1
;
const
actual
=
wrapper
.
find
(
'.ams-table-header .ams-table-heading-cell'
).
length
;
expect
(
actual
).
toEqual
(
expected
);
});
});
describe
(
'table body'
,
()
=>
{
it
(
'renders a table body'
,
()
=>
{
const
{
wrapper
}
=
setup
({
data
,
columns
,
});
const
expected
=
1
;
const
actual
=
wrapper
.
find
(
'.ams-table-body'
).
length
;
expect
(
actual
).
toEqual
(
expected
);
});
it
(
'renders one row'
,
()
=>
{
const
{
wrapper
}
=
setup
({
data
,
columns
,
});
const
expected
=
1
;
const
actual
=
wrapper
.
find
(
'.ams-table-row'
).
length
;
expect
(
actual
).
toEqual
(
expected
);
});
it
(
'renders an empty message'
,
()
=>
{
const
{
wrapper
}
=
setup
({
data
,
columns
,
});
const
expected
=
1
;
const
actual
=
wrapper
.
find
(
'.ams-table-row .ams-empty-message-cell'
)
.
length
;
expect
(
actual
).
toEqual
(
expected
);
});
});
});
describe
(
'when simple data is passed'
,
()
=>
{
describe
(
'when simple data is passed'
,
()
=>
{
it
(
'renders a table'
,
()
=>
{
it
(
'renders a table'
,
()
=>
{
const
{
wrapper
}
=
setup
();
const
{
wrapper
}
=
setup
();
...
@@ -119,6 +196,137 @@ describe('Table', () => {
...
@@ -119,6 +196,137 @@ describe('Table', () => {
});
});
});
});
});
});
describe
(
'options'
,
()
=>
{
describe
(
'when a tableClassName is passed'
,
()
=>
{
it
(
'adds the class to the table'
,
()
=>
{
const
{
wrapper
}
=
setup
({
options
:
{
tableClassName
:
'test-class'
},
});
const
expected
=
1
;
const
actual
=
wrapper
.
find
(
'.test-class'
).
length
;
expect
(
actual
).
toEqual
(
expected
);
});
});
describe
(
'when isLoading is active'
,
()
=>
{
it
(
'renders a table'
,
()
=>
{
const
{
wrapper
}
=
setup
({
data
:
[],
columns
:
[],
options
:
{
isLoading
:
true
,
numLoadingBlocks
:
10
,
},
});
const
expected
=
1
;
const
actual
=
wrapper
.
find
(
'.ams-table'
).
length
;
expect
(
actual
).
toEqual
(
expected
);
});
describe
(
'table header'
,
()
=>
{
it
(
'renders a table header'
,
()
=>
{
const
{
wrapper
}
=
setup
({
data
:
[],
columns
:
[],
options
:
{
isLoading
:
true
,
},
});
const
expected
=
1
;
const
actual
=
wrapper
.
find
(
'.ams-table-header'
).
length
;
expect
(
actual
).
toEqual
(
expected
);
});
it
(
'renders one cell inside the header'
,
()
=>
{
const
{
wrapper
}
=
setup
({
data
:
[],
columns
:
[],
options
:
{
isLoading
:
true
,
numLoadingBlocks
:
10
,
},
});
const
expected
=
1
;
const
actual
=
wrapper
.
find
(
'.ams-table-header .ams-table-heading-loading-cell'
).
length
;
expect
(
actual
).
toEqual
(
expected
);
});
it
(
'renders one loading block inside the header'
,
()
=>
{
const
{
wrapper
}
=
setup
({
data
:
[],
columns
:
[],
options
:
{
isLoading
:
true
,
numLoadingBlocks
:
10
,
},
});
const
expected
=
1
;
const
actual
=
wrapper
.
find
(
'.ams-table-header .ams-table-shimmer-block'
).
length
;
expect
(
actual
).
toEqual
(
expected
);
});
});
describe
(
'table body'
,
()
=>
{
it
(
'renders a table body'
,
()
=>
{
const
{
wrapper
}
=
setup
({
data
:
[],
columns
:
[],
options
:
{
isLoading
:
true
,
numLoadingBlocks
:
10
,
},
});
const
expected
=
1
;
const
actual
=
wrapper
.
find
(
'.ams-table-body'
).
length
;
expect
(
actual
).
toEqual
(
expected
);
});
it
(
'renders one row'
,
()
=>
{
const
{
wrapper
}
=
setup
({
data
:
[],
columns
:
[],
options
:
{
isLoading
:
true
,
numLoadingBlocks
:
10
,
},
});
const
expected
=
1
;
const
actual
=
wrapper
.
find
(
'.ams-table-row'
).
length
;
expect
(
actual
).
toEqual
(
expected
);
});
it
(
'renders the proper number of shimmering blocks'
,
()
=>
{
const
numOfLoadingBlocks
=
10
;
const
{
wrapper
}
=
setup
({
data
:
[],
columns
:
[],
options
:
{
isLoading
:
true
,
numLoadingBlocks
:
numOfLoadingBlocks
,
},
});
const
expected
=
numOfLoadingBlocks
;
const
actual
=
wrapper
.
find
(
'.ams-table-row .shimmer-resource-loader-item'
).
length
;
expect
(
actual
).
toEqual
(
expected
);
});
});
});
});
});
});
describe
(
'lifetime'
,
()
=>
{});
describe
(
'lifetime'
,
()
=>
{});
...
...
amundsen_application/static/js/components/common/Table/index.tsx
View file @
b2230452
...
@@ -3,55 +3,121 @@
...
@@ -3,55 +3,121 @@
import
*
as
React
from
'react'
;
import
*
as
React
from
'react'
;
import
ShimmeringResourceLoader
from
'../ShimmeringResourceLoader'
;
import
'./styles.scss'
;
import
'./styles.scss'
;
export
interface
TableColumn
{
export
interface
TableColumn
{
title
:
string
;
title
:
string
;
field
:
string
;
field
:
string
;
horAlign
?:
'left'
|
'right'
|
'center'
;
// width?: number;
// className?: string;
// className?: string;
// horAlign?: 'left' | 'right' | 'center';
// width?: number;
// sortable?: bool (false)
// sortable?: bool (false)
// data?: () => React.ReactNode ((row,index) => <div>{index}</div>)
// data?: () => React.ReactNode ((row,index) => <div>{index}</div>)
// actions?: Action[]
// actions?: Action[]
}
}
export
interface
TableOptions
{
tableClassName
?:
string
;
isLoading
?:
boolean
;
numLoadingBlocks
?:
number
;
}
export
interface
TableProps
{
export
interface
TableProps
{
columns
:
TableColumn
[];
columns
:
TableColumn
[];
data
:
[];
data
:
[];
options
?:
TableOptions
;
}
}
const
Table
:
React
.
FC
<
TableProps
>
=
({
data
,
columns
}:
TableProps
)
=>
{
const
DEFAULT_EMPTY_MESSAGE
=
'No Results'
;
const
DEFAULT_LOADING_ITEMS
=
3
;
type
EmptyRowProps
=
{
colspan
:
number
;
};
const
EmptyRow
:
React
.
FC
<
EmptyRowProps
>
=
({
colspan
}:
EmptyRowProps
)
=>
(
<
tr
className=
"ams-table-row"
>
<
td
className=
"ams-empty-message-cell"
colSpan=
{
colspan
}
>
{
DEFAULT_EMPTY_MESSAGE
}
</
td
>
</
tr
>
);
const
ShimmeringHeader
:
React
.
FC
=
()
=>
(
<
tr
>
<
th
className=
"ams-table-heading-loading-cell"
>
<
div
className=
"ams-table-shimmer-block"
/>
</
th
>
</
tr
>
);
type
ShimmeringBodyProps
=
{
numLoadingBlocks
:
number
;
};
const
ShimmeringBody
:
React
.
FC
<
ShimmeringBodyProps
>
=
({
numLoadingBlocks
,
}:
ShimmeringBodyProps
)
=>
(
<
tr
className=
"ams-table-row"
>
<
td
className=
"ams-table-body-loading-cell"
>
<
ShimmeringResourceLoader
numItems=
{
numLoadingBlocks
}
/>
</
td
>
</
tr
>
);
const
Table
:
React
.
FC
<
TableProps
>
=
({
data
,
columns
,
options
=
{},
}:
TableProps
)
=>
{
const
{
tableClassName
=
''
,
isLoading
=
false
,
numLoadingBlocks
=
DEFAULT_LOADING_ITEMS
,
}
=
options
;
const
fields
=
columns
.
map
(({
field
})
=>
field
);
const
fields
=
columns
.
map
(({
field
})
=>
field
);
return
(
let
body
:
React
.
ReactNode
=
<
EmptyRow
colspan=
{
fields
.
length
}
/>;
<
table
className=
"ams-table"
>
<
thead
className=
"ams-table-header"
>
if
(
data
.
length
)
{
<
tr
>
body
=
data
.
map
((
item
,
index
)
=>
{
{
columns
.
map
(({
title
},
index
)
=>
{
return
(
return
(
<
tr
className=
"ams-table-row"
key=
{
`index:${index}`
}
>
<
th
className=
"ams-table-heading-cell"
key=
{
`index:${index}`
}
>
{
Object
.
entries
(
item
)
{
title
}
.
filter
(([
key
])
=>
fields
.
includes
(
key
))
</
th
>
.
map
(([,
value
],
index
)
=>
(
);
<
td
className=
"ams-table-cell"
key=
{
`index:${index}`
}
>
})
}
{
value
}
</
td
>
))
}
</
tr
>
</
tr
>
</
thead
>
);
<
tbody
className=
"ams-table-body"
>
});
{
data
.
map
((
item
,
index
)
=>
{
}
return
(
<
tr
className=
"ams-table-row"
key=
{
`index:${index}`
}
>
let
header
:
React
.
ReactNode
=
(
{
Object
.
entries
(
item
)
<
tr
>
.
filter
(([
key
])
=>
fields
.
includes
(
key
))
{
columns
.
map
(({
title
},
index
)
=>
{
.
map
(([,
value
],
index
)
=>
(
return
(
<
td
className=
"ams-table-cell"
key=
{
`index:${index}`
}
>
<
th
className=
"ams-table-heading-cell"
key=
{
`index:${index}`
}
>
{
value
}
{
title
}
</
td
>
</
th
>
))
}
);
</
tr
>
})
}
);
</
tr
>
})
}
);
</
tbody
>
if
(
isLoading
)
{
header
=
<
ShimmeringHeader
/>;
body
=
<
ShimmeringBody
numLoadingBlocks=
{
numLoadingBlocks
}
/>;
}
return
(
<
table
className=
{
`ams-table ${tableClassName || ''}`
}
>
<
thead
className=
"ams-table-header"
>
{
header
}
</
thead
>
<
tbody
className=
"ams-table-body"
>
{
body
}
</
tbody
>
</
table
>
</
table
>
);
);
};
};
...
...
amundsen_application/static/js/components/common/Table/styles.scss
View file @
b2230452
// Copyright Contributors to the Amundsen project.
// SPDX-License-Identifier: Apache-2.0
@import
'variables'
;
@import
'variables'
;
@import
'typography'
;
@import
'typography'
;
@import
'animations'
;
$table-header-font-size
:
12px
;
$table-header-font-size
:
12px
;
$table-header-line-height
:
16px
;
$table-header-line-height
:
16px
;
$table-header-height
:
33px
;
$table-header-height
:
33px
;
$table-header-border-width
:
2px
;
$table-header-border-width
:
2px
;
$shimmer-block-height
:
16px
;
$shimmer-block-width
:
40%
;
.ams-table
{
.ams-table
{
width
:
100%
;
width
:
100%
;
max-width
:
100%
;
max-width
:
100%
;
...
@@ -24,3 +31,29 @@ $table-header-border-width: 2px;
...
@@ -24,3 +31,29 @@ $table-header-border-width: 2px;
height
:
$table-header-height
;
height
:
$table-header-height
;
text-transform
:
uppercase
;
text-transform
:
uppercase
;
}
}
.ams-empty-message-cell
{
@extend
%text-body-w3
;
color
:
$text-primary
;
text-align
:
center
;
}
.ams-table-heading-loading-cell
{
padding
:
$spacer-1
;
}
.ams-table-body-loading-cell
{
padding
:
0
$spacer-1
;
.shimmer-resource-loader
{
margin-top
:
-1px
;
}
}
.ams-table-shimmer-block
{
@extend
%is-shimmer-animated
;
height
:
$shimmer-block-height
;
width
:
$shimmer-block-width
;
}
amundsen_application/static/js/components/common/Table/table.story.tsx
View file @
b2230452
...
@@ -17,5 +17,11 @@ stories.add('Table', () => (
...
@@ -17,5 +17,11 @@ stories.add('Table', () => (
<
StorySection
title=
"Basic Table"
>
<
StorySection
title=
"Basic Table"
>
<
Table
columns=
{
columns
}
data=
{
data
}
/>
<
Table
columns=
{
columns
}
data=
{
data
}
/>
</
StorySection
>
</
StorySection
>
<
StorySection
title=
"Empty Table"
>
<
Table
columns=
{
columns
}
data=
{
[]
}
/>
</
StorySection
>
<
StorySection
title=
"Loading Table"
>
<
Table
columns=
{
[]
}
data=
{
[]
}
options=
{
{
isLoading
:
true
}
}
/>
</
StorySection
>
</>
</>
));
));
amundsen_application/static/js/components/common/Table/testDataBuilder.ts
View file @
b2230452
...
@@ -50,6 +50,15 @@ function TestDataBuilder(config = {}) {
...
@@ -50,6 +50,15 @@ function TestDataBuilder(config = {}) {
return
new
this
.
Klass
(
attr
);
return
new
this
.
Klass
(
attr
);
};
};
this
.
withEmptyData
=
()
=>
{
const
attr
=
{
data
:
[],
columns
:
[{
title
:
'Name'
,
field
:
'name'
}],
};
return
new
this
.
Klass
(
attr
);
};
this
.
withMoreDataThanColumns
=
()
=>
{
this
.
withMoreDataThanColumns
=
()
=>
{
const
attr
=
{
const
attr
=
{
data
:
[
data
:
[
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment