mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-18 14:13:37 +08:00
New component Descriptions (#14645)
* add new component: DescriptionList * add warning message * docs: fix doc typo * feat: implement the size attribute * docs: fix doc typo * refactor: use new name Descriptions * test: snapshots updated * feat: support react15 * style: fix code style warring * style: better var name * style: better code style * style: merge css class * feat: add responsive config * fix: fix error title * style: use @border-radius-base * update snapshot * feat: set default column * test: add test script * style: fix property defaultProps is useless error * style: more robust code * style: fix codereview warning * style: fix review warning * use responsiveObserveserve * fix review warning * bug: add childrenArray copy,prevent changes to incoming parameters * fix dom error * fix typo * fix test * don't use this * snapshot updated * prettier md * remove descriptions md text * new rendering method * doc :add dot * style: add right border
This commit is contained in:
parent
633fd7142d
commit
3e32364dc0
@ -21,6 +21,7 @@ Array [
|
||||
"Comment",
|
||||
"ConfigProvider",
|
||||
"DatePicker",
|
||||
"Descriptions",
|
||||
"Divider",
|
||||
"Dropdown",
|
||||
"Drawer",
|
||||
|
101
components/_util/responsiveObserve.ts
Normal file
101
components/_util/responsiveObserve.ts
Normal file
@ -0,0 +1,101 @@
|
||||
// matchMedia polyfill for
|
||||
// https://github.com/WickyNilliams/enquire.js/issues/82
|
||||
let enquire: any;
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
const matchMediaPolyfill = (mediaQuery: string) => {
|
||||
return {
|
||||
media: mediaQuery,
|
||||
matches: false,
|
||||
addListener() {},
|
||||
removeListener() {},
|
||||
};
|
||||
};
|
||||
window.matchMedia = window.matchMedia || matchMediaPolyfill;
|
||||
enquire = require('enquire.js');
|
||||
}
|
||||
|
||||
export type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
|
||||
export type BreakpointMap = Partial<Record<Breakpoint, string>>;
|
||||
|
||||
export const responsiveArray: Breakpoint[] = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
|
||||
|
||||
export const responsiveMap: BreakpointMap = {
|
||||
xs: '(max-width: 575px)',
|
||||
sm: '(min-width: 576px)',
|
||||
md: '(min-width: 768px)',
|
||||
lg: '(min-width: 992px)',
|
||||
xl: '(min-width: 1200px)',
|
||||
xxl: '(min-width: 1600px)',
|
||||
};
|
||||
|
||||
type SubscribeFunc = (screens: BreakpointMap) => void;
|
||||
|
||||
let subscribers: Array<{
|
||||
token: string;
|
||||
func: SubscribeFunc;
|
||||
}> = [];
|
||||
let subUid = -1;
|
||||
let screens = {};
|
||||
|
||||
const responsiveObserve = {
|
||||
dispatch(pointMap: BreakpointMap) {
|
||||
screens = pointMap;
|
||||
if (subscribers.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
subscribers.forEach(item => {
|
||||
item.func(screens);
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
subscribe(func: SubscribeFunc) {
|
||||
if (subscribers.length === 0) {
|
||||
this.register();
|
||||
}
|
||||
const token = (++subUid).toString();
|
||||
subscribers.push({
|
||||
token: token,
|
||||
func: func,
|
||||
});
|
||||
func(screens);
|
||||
return token;
|
||||
},
|
||||
unsubscribe(token: string) {
|
||||
subscribers = subscribers.filter(item => item.token !== token);
|
||||
if (subscribers.length === 0) {
|
||||
this.unregister();
|
||||
}
|
||||
},
|
||||
unregister() {
|
||||
Object.keys(responsiveMap).map((screen: Breakpoint) =>
|
||||
enquire.unregister(responsiveMap[screen]),
|
||||
);
|
||||
},
|
||||
register() {
|
||||
Object.keys(responsiveMap).map((screen: Breakpoint) =>
|
||||
enquire.register(responsiveMap[screen], {
|
||||
match: () => {
|
||||
const pointMap = {
|
||||
...screens,
|
||||
[screen]: true,
|
||||
};
|
||||
this.dispatch(pointMap);
|
||||
},
|
||||
unmatch: () => {
|
||||
const pointMap = {
|
||||
...screens,
|
||||
[screen]: false,
|
||||
};
|
||||
this.dispatch(pointMap);
|
||||
},
|
||||
// Keep a empty destory to avoid triggering unmatch when unregister
|
||||
destroy() {},
|
||||
}),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default responsiveObserve;
|
@ -0,0 +1,611 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/descriptions/demo/basic.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-descriptions"
|
||||
>
|
||||
<div
|
||||
class="ant-descriptions-title"
|
||||
>
|
||||
User Info
|
||||
</div>
|
||||
<div
|
||||
class="ant-descriptions-view"
|
||||
>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr
|
||||
class="ant-descriptions-row"
|
||||
>
|
||||
<td
|
||||
class="ant-descriptions-item"
|
||||
colspan="1"
|
||||
>
|
||||
<span
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
UserName
|
||||
</span>
|
||||
<span
|
||||
class="ant-descriptions-item-content"
|
||||
>
|
||||
Zhou Maomao
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item"
|
||||
colspan="1"
|
||||
>
|
||||
<span
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Telephone
|
||||
</span>
|
||||
<span
|
||||
class="ant-descriptions-item-content"
|
||||
>
|
||||
1810000000
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item"
|
||||
colspan="1"
|
||||
>
|
||||
<span
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Live
|
||||
</span>
|
||||
<span
|
||||
class="ant-descriptions-item-content"
|
||||
>
|
||||
Hangzhou, Zhejiang
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-descriptions-row"
|
||||
>
|
||||
<td
|
||||
class="ant-descriptions-item"
|
||||
colspan="1"
|
||||
>
|
||||
<span
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Remark
|
||||
</span>
|
||||
<span
|
||||
class="ant-descriptions-item-content"
|
||||
>
|
||||
empty
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item"
|
||||
colspan="2"
|
||||
>
|
||||
<span
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Address
|
||||
</span>
|
||||
<span
|
||||
class="ant-descriptions-item-content"
|
||||
>
|
||||
No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/descriptions/demo/border.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-descriptions bordered"
|
||||
>
|
||||
<div
|
||||
class="ant-descriptions-title"
|
||||
>
|
||||
User Info
|
||||
</div>
|
||||
<div
|
||||
class="ant-descriptions-view"
|
||||
>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr
|
||||
class="ant-descriptions-row"
|
||||
>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Product
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="1"
|
||||
>
|
||||
Cloud Database
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Billing Mode
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="1"
|
||||
>
|
||||
Prepaid
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Automatic Renewal
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="1"
|
||||
>
|
||||
YES
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-descriptions-row"
|
||||
>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Order time
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="1"
|
||||
>
|
||||
2018-04-24 18:00:00
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Usage Time
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="5"
|
||||
>
|
||||
2019-04-24 18:00:00
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-descriptions-row"
|
||||
>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Status
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="5"
|
||||
>
|
||||
<span
|
||||
class="ant-badge ant-badge-status ant-badge-not-a-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-badge-status-dot ant-badge-status-processing"
|
||||
/>
|
||||
<span
|
||||
class="ant-badge-status-text"
|
||||
>
|
||||
Running
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-descriptions-row"
|
||||
>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Negotiated Amount
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="1"
|
||||
>
|
||||
$80.00
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Discount
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="1"
|
||||
>
|
||||
$20.00
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Official Receipts
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="1"
|
||||
>
|
||||
$60.00
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-descriptions-row"
|
||||
>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Config Info
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="5"
|
||||
>
|
||||
Data disk type: MongoDB
|
||||
<br />
|
||||
Database version: 3.4
|
||||
<br />
|
||||
Package: dds.mongo.mid
|
||||
<br />
|
||||
Storage space: 10 GB
|
||||
<br />
|
||||
Replication_factor:3
|
||||
<br />
|
||||
Region: East China 1
|
||||
<br />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/descriptions/demo/responsive.md correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="ant-descriptions"
|
||||
>
|
||||
<div
|
||||
class="ant-descriptions-title"
|
||||
>
|
||||
Responsive Descriptions
|
||||
</div>
|
||||
<div
|
||||
class="ant-descriptions-view"
|
||||
>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr
|
||||
class="ant-descriptions-row"
|
||||
>
|
||||
<td
|
||||
class="ant-descriptions-item"
|
||||
colspan="1"
|
||||
>
|
||||
<span
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Product
|
||||
</span>
|
||||
<span
|
||||
class="ant-descriptions-item-content"
|
||||
>
|
||||
Cloud Database
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item"
|
||||
colspan="1"
|
||||
>
|
||||
<span
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Billing
|
||||
</span>
|
||||
<span
|
||||
class="ant-descriptions-item-content"
|
||||
>
|
||||
Prepaid
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item"
|
||||
colspan="1"
|
||||
>
|
||||
<span
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
time
|
||||
</span>
|
||||
<span
|
||||
class="ant-descriptions-item-content"
|
||||
>
|
||||
18:00:00
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-descriptions-row"
|
||||
>
|
||||
<td
|
||||
class="ant-descriptions-item"
|
||||
colspan="1"
|
||||
>
|
||||
<span
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Amount
|
||||
</span>
|
||||
<span
|
||||
class="ant-descriptions-item-content"
|
||||
>
|
||||
$80.00
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item"
|
||||
colspan="1"
|
||||
>
|
||||
<span
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Discount
|
||||
</span>
|
||||
<span
|
||||
class="ant-descriptions-item-content"
|
||||
>
|
||||
$20.00
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item"
|
||||
colspan="1"
|
||||
>
|
||||
<span
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Official
|
||||
</span>
|
||||
<span
|
||||
class="ant-descriptions-item-content"
|
||||
>
|
||||
$60.00
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-descriptions-row"
|
||||
>
|
||||
<td
|
||||
class="ant-descriptions-item"
|
||||
colspan="3"
|
||||
>
|
||||
<span
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Config Info
|
||||
</span>
|
||||
<span
|
||||
class="ant-descriptions-item-content"
|
||||
>
|
||||
Data disk type: MongoDB
|
||||
<br />
|
||||
Database version: 3.4
|
||||
<br />
|
||||
Package: dds.mongo.mid
|
||||
<br />
|
||||
Storage space: 10 GB
|
||||
<br />
|
||||
Replication_factor:3
|
||||
<br />
|
||||
Region: East China 1
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-wrapper ant-radio-wrapper-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-radio ant-radio-checked"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value="default"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
default
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value="middle"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
middle
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
type="radio"
|
||||
value="small"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
small
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div
|
||||
class="ant-descriptions bordered"
|
||||
>
|
||||
<div
|
||||
class="ant-descriptions-title"
|
||||
>
|
||||
Custom Size
|
||||
</div>
|
||||
<div
|
||||
class="ant-descriptions-view"
|
||||
>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr
|
||||
class="ant-descriptions-row"
|
||||
>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Product
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="1"
|
||||
>
|
||||
Cloud Database
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Billing
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="1"
|
||||
>
|
||||
Prepaid
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
time
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="1"
|
||||
>
|
||||
18:00:00
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-descriptions-row"
|
||||
>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Amount
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="1"
|
||||
>
|
||||
$80.00
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Discount
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="1"
|
||||
>
|
||||
$20.00
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Official
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="1"
|
||||
>
|
||||
$60.00
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-descriptions-row"
|
||||
>
|
||||
<td
|
||||
class="ant-descriptions-item-label"
|
||||
>
|
||||
Config Info
|
||||
</td>
|
||||
<td
|
||||
class="ant-descriptions-item-content"
|
||||
colspan="5"
|
||||
>
|
||||
Data disk type: MongoDB
|
||||
<br />
|
||||
Database version: 3.4
|
||||
<br />
|
||||
Package: dds.mongo.mid
|
||||
<br />
|
||||
Storage space: 10 GB
|
||||
<br />
|
||||
Replication_factor:3
|
||||
<br />
|
||||
Region: East China 1
|
||||
<br />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -0,0 +1,101 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Descriptions column is number 1`] = `
|
||||
<Descriptions
|
||||
column="3"
|
||||
size="default"
|
||||
>
|
||||
<div
|
||||
className="ant-descriptions"
|
||||
>
|
||||
<div
|
||||
className="ant-descriptions-view"
|
||||
>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr
|
||||
className="ant-descriptions-row"
|
||||
key="0"
|
||||
>
|
||||
<td
|
||||
className="ant-descriptions-item"
|
||||
colSpan={1}
|
||||
key=".$.0"
|
||||
>
|
||||
<span
|
||||
className="ant-descriptions-item-label"
|
||||
key="label"
|
||||
>
|
||||
Product
|
||||
</span>
|
||||
<span
|
||||
className="ant-descriptions-item-content"
|
||||
key="content"
|
||||
>
|
||||
Cloud Database
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
className="ant-descriptions-item"
|
||||
colSpan={1}
|
||||
key=".$.1"
|
||||
>
|
||||
<span
|
||||
className="ant-descriptions-item-label"
|
||||
key="label"
|
||||
>
|
||||
Billing
|
||||
</span>
|
||||
<span
|
||||
className="ant-descriptions-item-content"
|
||||
key="content"
|
||||
>
|
||||
Prepaid
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
className="ant-descriptions-item"
|
||||
colSpan={1}
|
||||
>
|
||||
<span
|
||||
className="ant-descriptions-item-label"
|
||||
key="label"
|
||||
>
|
||||
time
|
||||
</span>
|
||||
<span
|
||||
className="ant-descriptions-item-content"
|
||||
key="content"
|
||||
>
|
||||
18:00:00
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
className="ant-descriptions-row"
|
||||
key="1"
|
||||
>
|
||||
<td
|
||||
className="ant-descriptions-item"
|
||||
colSpan={3}
|
||||
>
|
||||
<span
|
||||
className="ant-descriptions-item-label"
|
||||
key="label"
|
||||
>
|
||||
Amount
|
||||
</span>
|
||||
<span
|
||||
className="ant-descriptions-item-content"
|
||||
key="content"
|
||||
>
|
||||
$80.00
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</Descriptions>
|
||||
`;
|
3
components/descriptions/__tests__/demo.test.js
Normal file
3
components/descriptions/__tests__/demo.test.js
Normal file
@ -0,0 +1,3 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('descriptions');
|
86
components/descriptions/__tests__/index.test.js
Normal file
86
components/descriptions/__tests__/index.test.js
Normal file
@ -0,0 +1,86 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Descriptions from '..';
|
||||
|
||||
const DescriptionsItem = Descriptions.Item;
|
||||
|
||||
jest.mock('enquire.js', () => {
|
||||
let that;
|
||||
let unmatchFun;
|
||||
return {
|
||||
unregister: jest.fn(),
|
||||
register: (media, options) => {
|
||||
if (media === '(max-width: 575px)') {
|
||||
that = this;
|
||||
options.match.call(that);
|
||||
unmatchFun = options.unmatch;
|
||||
}
|
||||
},
|
||||
callunmatch() {
|
||||
unmatchFun.call(that);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('Descriptions', () => {
|
||||
it('when max-width: 575px,column=1', () => {
|
||||
// eslint-disable-next-line global-require
|
||||
const enquire = require('enquire.js');
|
||||
const wrapper = mount(
|
||||
<Descriptions>
|
||||
<DescriptionsItem label="Product">Cloud Database</DescriptionsItem>
|
||||
<DescriptionsItem label="Billing">Prepaid</DescriptionsItem>
|
||||
<DescriptionsItem label="time">18:00:00</DescriptionsItem>
|
||||
<DescriptionsItem label="Amount">$80.00</DescriptionsItem>
|
||||
</Descriptions>,
|
||||
);
|
||||
expect(wrapper.find('tr')).toHaveLength(4);
|
||||
|
||||
enquire.callunmatch();
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('when max-width: 575px,column=2', () => {
|
||||
// eslint-disable-next-line global-require
|
||||
const enquire = require('enquire.js');
|
||||
const wrapper = mount(
|
||||
<Descriptions column={{ xs: 2 }}>
|
||||
<DescriptionsItem label="Product">Cloud Database</DescriptionsItem>
|
||||
<DescriptionsItem label="Billing">Prepaid</DescriptionsItem>
|
||||
<DescriptionsItem label="time">18:00:00</DescriptionsItem>
|
||||
<DescriptionsItem label="Amount">$80.00</DescriptionsItem>
|
||||
</Descriptions>,
|
||||
);
|
||||
expect(wrapper.find('tr')).toHaveLength(2);
|
||||
|
||||
enquire.callunmatch();
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('column is number', () => {
|
||||
// eslint-disable-next-line global-require
|
||||
const wrapper = mount(
|
||||
<Descriptions column="3">
|
||||
<DescriptionsItem label="Product">Cloud Database</DescriptionsItem>
|
||||
<DescriptionsItem label="Billing">Prepaid</DescriptionsItem>
|
||||
<DescriptionsItem label="time">18:00:00</DescriptionsItem>
|
||||
<DescriptionsItem label="Amount">$80.00</DescriptionsItem>
|
||||
</Descriptions>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('when typeof column is object', () => {
|
||||
const wrapper = mount(
|
||||
<Descriptions column={{ xs: 8, sm: 16, md: 24 }}>
|
||||
<DescriptionsItem label="Product">Cloud Database</DescriptionsItem>
|
||||
<DescriptionsItem label="Billing">Prepaid</DescriptionsItem>
|
||||
<DescriptionsItem label="time">18:00:00</DescriptionsItem>
|
||||
<DescriptionsItem label="Amount">$80.00</DescriptionsItem>
|
||||
</Descriptions>,
|
||||
);
|
||||
expect(wrapper.instance().getColumn()).toBe(8);
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
33
components/descriptions/demo/basic.md
Normal file
33
components/descriptions/demo/basic.md
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
order: 0
|
||||
title:
|
||||
zh-CN: 基本
|
||||
en-US: Basic
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
简单的展示。
|
||||
|
||||
## en-US
|
||||
|
||||
Simplest Usage.
|
||||
|
||||
```jsx
|
||||
import { Descriptions } from 'antd';
|
||||
|
||||
const DescriptionsItem = Descriptions.Item;
|
||||
|
||||
ReactDOM.render(
|
||||
<Descriptions title="User Info">
|
||||
<DescriptionsItem label="UserName">Zhou Maomao</DescriptionsItem>
|
||||
<DescriptionsItem label="Telephone">1810000000</DescriptionsItem>
|
||||
<DescriptionsItem label="Live">Hangzhou, Zhejiang</DescriptionsItem>
|
||||
<DescriptionsItem label="Remark">empty</DescriptionsItem>
|
||||
<DescriptionsItem label="Address">
|
||||
No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China
|
||||
</DescriptionsItem>
|
||||
</Descriptions>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
52
components/descriptions/demo/border.md
Normal file
52
components/descriptions/demo/border.md
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
order: 1
|
||||
title:
|
||||
zh-CN: 带边框的
|
||||
en-US: border
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
带边框和背景颜色列表。
|
||||
|
||||
## en-US
|
||||
|
||||
Descriptions with border and background color.
|
||||
|
||||
```jsx
|
||||
import { Descriptions, Badge } from 'antd';
|
||||
|
||||
const DescriptionsItem = Descriptions.Item;
|
||||
|
||||
ReactDOM.render(
|
||||
<Descriptions title="User Info" bordered>
|
||||
<DescriptionsItem label="Product">Cloud Database</DescriptionsItem>
|
||||
<DescriptionsItem label="Billing Mode">Prepaid</DescriptionsItem>
|
||||
<DescriptionsItem label="Automatic Renewal">YES</DescriptionsItem>
|
||||
<DescriptionsItem label="Order time">2018-04-24 18:00:00</DescriptionsItem>
|
||||
<DescriptionsItem label="Usage Time" span={3}>
|
||||
2019-04-24 18:00:00
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label="Status" span={3}>
|
||||
<Badge status="processing" text="Running" />
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label="Negotiated Amount">$80.00</DescriptionsItem>
|
||||
<DescriptionsItem label="Discount">$20.00</DescriptionsItem>
|
||||
<DescriptionsItem label="Official Receipts">$60.00</DescriptionsItem>
|
||||
<DescriptionsItem label="Config Info">
|
||||
Data disk type: MongoDB
|
||||
<br />
|
||||
Database version: 3.4
|
||||
<br />
|
||||
Package: dds.mongo.mid
|
||||
<br />
|
||||
Storage space: 10 GB
|
||||
<br />
|
||||
Replication_factor:3
|
||||
<br />
|
||||
Region: East China 1<br />
|
||||
</DescriptionsItem>
|
||||
</Descriptions>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
54
components/descriptions/demo/responsive.md
Normal file
54
components/descriptions/demo/responsive.md
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
order: 3
|
||||
title:
|
||||
zh-CN: 响应式
|
||||
en-US: responsive
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
通过响应式的配置可以实现在小屏幕设备上的完美呈现。
|
||||
|
||||
## en-US
|
||||
|
||||
Responsive configuration enables perfect presentation on small screen devices.
|
||||
|
||||
```jsx
|
||||
import { Descriptions } from 'antd';
|
||||
|
||||
const DescriptionsItem = Descriptions.Item;
|
||||
|
||||
const Demo = () => {
|
||||
return (
|
||||
<div>
|
||||
<Descriptions
|
||||
title="Responsive Descriptions"
|
||||
border
|
||||
column={{ xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1 }}
|
||||
>
|
||||
<DescriptionsItem label="Product">Cloud Database</DescriptionsItem>
|
||||
<DescriptionsItem label="Billing">Prepaid</DescriptionsItem>
|
||||
<DescriptionsItem label="time">18:00:00</DescriptionsItem>
|
||||
<DescriptionsItem label="Amount">$80.00</DescriptionsItem>
|
||||
<DescriptionsItem label="Discount">$20.00</DescriptionsItem>
|
||||
<DescriptionsItem label="Official">$60.00</DescriptionsItem>
|
||||
<DescriptionsItem label="Config Info">
|
||||
Data disk type: MongoDB
|
||||
<br />
|
||||
Database version: 3.4
|
||||
<br />
|
||||
Package: dds.mongo.mid
|
||||
<br />
|
||||
Storage space: 10 GB
|
||||
<br />
|
||||
Replication_factor:3
|
||||
<br />
|
||||
Region: East China 1
|
||||
</DescriptionsItem>
|
||||
</Descriptions>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
```
|
72
components/descriptions/demo/size.md
Normal file
72
components/descriptions/demo/size.md
Normal file
@ -0,0 +1,72 @@
|
||||
---
|
||||
order: 2
|
||||
title:
|
||||
zh-CN: 自定义尺寸
|
||||
en-US: Custom size
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
自定义尺寸,适应在各种容器中展示。
|
||||
|
||||
## en-US
|
||||
|
||||
Custom sizes to fit in a variety of containers.
|
||||
|
||||
```jsx
|
||||
import { Descriptions, Radio } from 'antd';
|
||||
|
||||
const RadioGroup = Radio.Group;
|
||||
|
||||
const DescriptionsItem = Descriptions.Item;
|
||||
|
||||
class Demo extends React.Component {
|
||||
state = {
|
||||
size: 'default',
|
||||
};
|
||||
|
||||
onChange = e => {
|
||||
console.log('size checked', e.target.value);
|
||||
this.setState({
|
||||
size: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<RadioGroup onChange={this.onChange} value={this.state.size}>
|
||||
<Radio value="default">default</Radio>
|
||||
<Radio value="middle">middle</Radio>
|
||||
<Radio value="small">small</Radio>
|
||||
</RadioGroup>
|
||||
<br />
|
||||
<br />
|
||||
<Descriptions bordered title="Custom Size" border size={this.state.size}>
|
||||
<DescriptionsItem label="Product">Cloud Database</DescriptionsItem>
|
||||
<DescriptionsItem label="Billing">Prepaid</DescriptionsItem>
|
||||
<DescriptionsItem label="time">18:00:00</DescriptionsItem>
|
||||
<DescriptionsItem label="Amount">$80.00</DescriptionsItem>
|
||||
<DescriptionsItem label="Discount">$20.00</DescriptionsItem>
|
||||
<DescriptionsItem label="Official">$60.00</DescriptionsItem>
|
||||
<DescriptionsItem label="Config Info">
|
||||
Data disk type: MongoDB
|
||||
<br />
|
||||
Database version: 3.4
|
||||
<br />
|
||||
Package: dds.mongo.mid
|
||||
<br />
|
||||
Storage space: 10 GB
|
||||
<br />
|
||||
Replication_factor:3
|
||||
<br />
|
||||
Region: East China 1<br />
|
||||
</DescriptionsItem>
|
||||
</Descriptions>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
```
|
30
components/descriptions/index.en-US.md
Normal file
30
components/descriptions/index.en-US.md
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
category: Components
|
||||
type: Data Display
|
||||
title: Description List
|
||||
cols: 1
|
||||
---
|
||||
|
||||
Display multiple read-only fields in groups.
|
||||
|
||||
## When To Use
|
||||
|
||||
Commonly displayed on the details page.
|
||||
|
||||
## API
|
||||
|
||||
### Descriptions
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| title | The title of the description list, placed at the top | ReactNode | - |
|
||||
| bordered | whether to display the border | boolean | false |
|
||||
| column | the number of `DescriptionItems` in a row,could be a number or a object like `{ xs: 8, sm: 16, md: 24}`,(Only set `bordered={true}` to take effect) | number | 3 |
|
||||
| size | set the size of the list. Can be set to `middle`,`small`, or not filled | `default | middle | small` | false |
|
||||
|
||||
### DescriptionItem
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| -------- | ------------------------------ | --------- | ------- |
|
||||
| label | description of the content | ReactNode | - |
|
||||
| span | The number of columns included | number | 1 |
|
255
components/descriptions/index.tsx
Normal file
255
components/descriptions/index.tsx
Normal file
@ -0,0 +1,255 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import warning from '../_util/warning';
|
||||
import ResponsiveObserve, {
|
||||
Breakpoint,
|
||||
BreakpointMap,
|
||||
responsiveArray,
|
||||
} from '../_util/responsiveObserve';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
|
||||
export interface DescriptionsItemProps {
|
||||
prefixCls?: string;
|
||||
label: React.ReactNode;
|
||||
children: JSX.Element;
|
||||
span?: number;
|
||||
}
|
||||
|
||||
const DescriptionsItem: React.SFC<DescriptionsItemProps> = ({ children }) => children;
|
||||
|
||||
export interface DescriptionsProps {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
bordered?: boolean;
|
||||
size?: 'middle' | 'small' | 'default';
|
||||
children?: React.ReactNode;
|
||||
title?: string;
|
||||
column?: number | Partial<Record<Breakpoint, number>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert children into `column` groups.
|
||||
* @param cloneChildren: DescriptionsItem
|
||||
* @param column: number
|
||||
*/
|
||||
const generateChildrenRows = (
|
||||
cloneChildren: React.ReactNode,
|
||||
column: number,
|
||||
): React.ReactElement<DescriptionsItemProps>[][] => {
|
||||
const childrenArray: React.ReactElement<DescriptionsItemProps>[][] = [];
|
||||
let columnArray: React.ReactElement<DescriptionsItemProps>[] = [];
|
||||
let totalRowSpan = 0;
|
||||
React.Children.forEach(cloneChildren, (node: React.ReactElement<DescriptionsItemProps>) => {
|
||||
columnArray.push(node);
|
||||
if (node.props.span) {
|
||||
totalRowSpan += node.props.span;
|
||||
} else {
|
||||
totalRowSpan += 1;
|
||||
}
|
||||
if (totalRowSpan >= column) {
|
||||
childrenArray.push(columnArray);
|
||||
columnArray = [];
|
||||
totalRowSpan = 0;
|
||||
warning(
|
||||
totalRowSpan > column,
|
||||
'Descriptions',
|
||||
'Sum of column `span` in a line exceeds `column` of Descriptions.',
|
||||
);
|
||||
}
|
||||
});
|
||||
if (columnArray.length > 0) {
|
||||
childrenArray.push(columnArray);
|
||||
columnArray = [];
|
||||
}
|
||||
return childrenArray;
|
||||
};
|
||||
|
||||
/**
|
||||
* This code is for handling react15 does not support returning an array,
|
||||
* It can convert a children into two td
|
||||
* @param child DescriptionsItem
|
||||
* @returns
|
||||
* <>
|
||||
* <td>{DescriptionsItem.label}</td>
|
||||
* <td>{DescriptionsItem.children}</td>
|
||||
* </>
|
||||
*/
|
||||
const renderCol = (child: React.ReactElement<DescriptionsItemProps>, bordered: boolean) => {
|
||||
const { prefixCls, label, children, span = 1 } = child.props;
|
||||
if (bordered) {
|
||||
return [
|
||||
<td className={`${prefixCls}-item-label`} key="label">
|
||||
{label}
|
||||
</td>,
|
||||
<td className={`${prefixCls}-item-content`} key="content" colSpan={span * 2 - 1}>
|
||||
{children}
|
||||
</td>,
|
||||
];
|
||||
}
|
||||
return (
|
||||
<td colSpan={span} className={`${prefixCls}-item`}>
|
||||
<span className={`${prefixCls}-item-label`} key="label">
|
||||
{label}
|
||||
</span>
|
||||
<span className={`${prefixCls}-item-content`} key="content">
|
||||
{children}
|
||||
</span>
|
||||
</td>
|
||||
);
|
||||
};
|
||||
|
||||
const renderRow = (
|
||||
children: React.ReactElement<DescriptionsItemProps>[],
|
||||
index: number,
|
||||
{ prefixCls, column, isLast }: { prefixCls: string; column: number; isLast: boolean },
|
||||
bordered: boolean,
|
||||
) => {
|
||||
// copy children,prevent changes to incoming parameters
|
||||
const childrenArray = [...children];
|
||||
let lastChildren = childrenArray.pop() as React.ReactElement<DescriptionsItemProps>;
|
||||
const span = column - childrenArray.length;
|
||||
if (isLast) {
|
||||
lastChildren = React.cloneElement(lastChildren as React.ReactElement<DescriptionsItemProps>, {
|
||||
span,
|
||||
});
|
||||
}
|
||||
const cloneChildren = React.Children.map(
|
||||
childrenArray,
|
||||
(childrenItem: React.ReactElement<DescriptionsItemProps>) => {
|
||||
return renderCol(childrenItem, bordered);
|
||||
},
|
||||
);
|
||||
return (
|
||||
<tr className={`${prefixCls}-row`} key={index}>
|
||||
{cloneChildren}
|
||||
{renderCol(lastChildren, bordered)}
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
const defaultColumnMap = {
|
||||
xxl: 3,
|
||||
xl: 3,
|
||||
lg: 3,
|
||||
md: 3,
|
||||
sm: 2,
|
||||
xs: 1,
|
||||
};
|
||||
|
||||
class Descriptions extends React.Component<
|
||||
DescriptionsProps,
|
||||
{
|
||||
screens: BreakpointMap;
|
||||
}
|
||||
> {
|
||||
static defaultProps: DescriptionsProps = {
|
||||
size: 'default',
|
||||
column: defaultColumnMap,
|
||||
};
|
||||
static Item: typeof DescriptionsItem;
|
||||
state: {
|
||||
screens: BreakpointMap;
|
||||
} = {
|
||||
screens: {},
|
||||
};
|
||||
token: string;
|
||||
componentDidMount() {
|
||||
const { column } = this.props;
|
||||
this.token = ResponsiveObserve.subscribe(screens => {
|
||||
if (typeof column !== 'object') {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
screens,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
ResponsiveObserve.unsubscribe(this.token);
|
||||
}
|
||||
|
||||
getColumn(): number {
|
||||
const { column } = this.props;
|
||||
if (typeof column === 'object') {
|
||||
for (let i = 0; i < responsiveArray.length; i++) {
|
||||
const breakpoint: Breakpoint = responsiveArray[i];
|
||||
if (this.state.screens[breakpoint] && column[breakpoint] !== undefined) {
|
||||
return column[breakpoint] || defaultColumnMap[breakpoint];
|
||||
}
|
||||
}
|
||||
}
|
||||
//If the configuration is not an object, it is a number, return number
|
||||
if (typeof column === 'number') {
|
||||
return column as number;
|
||||
}
|
||||
// If it is an object, but no response is found, this happens only in the test.
|
||||
// Maybe there are some strange environments
|
||||
return 3;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ConfigConsumer>
|
||||
{({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const {
|
||||
className,
|
||||
prefixCls: customizePrefixCls,
|
||||
title,
|
||||
size,
|
||||
children,
|
||||
bordered = false,
|
||||
} = this.props;
|
||||
const prefixCls = getPrefixCls('descriptions', customizePrefixCls);
|
||||
|
||||
const column = this.getColumn();
|
||||
const cloneChildren = React.Children.map(
|
||||
children,
|
||||
(child: React.ReactElement<DescriptionsItemProps>) => {
|
||||
return React.cloneElement(child, {
|
||||
prefixCls,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const childrenArray: Array<
|
||||
React.ReactElement<DescriptionsItemProps>[]
|
||||
> = generateChildrenRows(cloneChildren, column);
|
||||
return (
|
||||
<div
|
||||
className={classNames(prefixCls, className, {
|
||||
[size as string]: size !== 'default',
|
||||
bordered,
|
||||
})}
|
||||
>
|
||||
{title && <div className={`${prefixCls}-title`}>{title}</div>}
|
||||
<div className={`${prefixCls}-view`}>
|
||||
<table>
|
||||
<tbody>
|
||||
{childrenArray.map((child, index) =>
|
||||
renderRow(
|
||||
child,
|
||||
index,
|
||||
{
|
||||
prefixCls,
|
||||
column,
|
||||
isLast: index + 1 === childrenArray.length,
|
||||
},
|
||||
bordered,
|
||||
),
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</ConfigConsumer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Descriptions.Item = DescriptionsItem;
|
||||
|
||||
export default Descriptions;
|
31
components/descriptions/index.zh-CN.md
Normal file
31
components/descriptions/index.zh-CN.md
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
category: Components
|
||||
subtitle: 描述列表
|
||||
type: 数据展示
|
||||
title: Descriptions
|
||||
cols: 1
|
||||
---
|
||||
|
||||
成组展示多个只读字段。
|
||||
|
||||
## 何时使用
|
||||
|
||||
常见于详情页的信息展示。
|
||||
|
||||
## API
|
||||
|
||||
### Descriptions
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| title | 描述列表的标题,显示在最顶部 | ReactNode | - |
|
||||
| bordered | 是否展示边框 | boolean | false |
|
||||
| column | 一行的 `DescriptionItems` 数量,可以写成像素值或支持响应式的对象写法 `{ xs: 8, sm: 16, md: 24}` | number | 3 |
|
||||
| size | 设置列表的大小。可以设置为 `middle` 、`small`, 或不填(只有设置 `bordered={true}` 生效) | `default | middle | small` | false |
|
||||
|
||||
### DescriptionItem
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| ----- | ------------ | --------- | ------ |
|
||||
| label | 内容的描述 | ReactNode | - |
|
||||
| span | 包含列的数量 | number | 1 |
|
118
components/descriptions/style/index.less
Normal file
118
components/descriptions/style/index.less
Normal file
@ -0,0 +1,118 @@
|
||||
@import '../../style/themes/default';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@descriptions-prefix-cls: ~'@{ant-prefix}-descriptions';
|
||||
|
||||
@descriptions-default-padding: 16px 24px;
|
||||
@descriptions-middle-padding: 12px 24px;
|
||||
@descriptions-small-padding: 8px 16px;
|
||||
|
||||
.@{descriptions-prefix-cls} {
|
||||
&-title {
|
||||
margin-bottom: 20px;
|
||||
color: @heading-color;
|
||||
font-weight: bold;
|
||||
font-size: @font-size-lg;
|
||||
line-height: @line-height-base;
|
||||
}
|
||||
|
||||
&-view {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
border-radius: @border-radius-base;
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&-row {
|
||||
> td {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-item-label {
|
||||
color: @heading-color;
|
||||
font-size: @font-size-base;
|
||||
line-height: @line-height-base;
|
||||
white-space: nowrap;
|
||||
&::after {
|
||||
position: relative;
|
||||
top: -0.5px;
|
||||
margin: 0 8px 0 2px;
|
||||
content: ':';
|
||||
}
|
||||
}
|
||||
|
||||
&-item-content {
|
||||
display: table-cell;
|
||||
color: @text-color;
|
||||
font-size: @font-size-base;
|
||||
line-height: @line-height-base;
|
||||
}
|
||||
|
||||
&-item {
|
||||
padding-bottom: 0;
|
||||
> span {
|
||||
display: inline-block;
|
||||
}
|
||||
.@{descriptions-prefix-cls}-item-label {
|
||||
float: left;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.@{descriptions-prefix-cls}-item-content {
|
||||
float: left;
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
// padding setting
|
||||
.@{descriptions-prefix-cls}-item-label,
|
||||
.@{descriptions-prefix-cls}-item-content {
|
||||
padding: @descriptions-default-padding;
|
||||
}
|
||||
|
||||
&.bordered.middle {
|
||||
.@{descriptions-prefix-cls}-item-label,
|
||||
.@{descriptions-prefix-cls}-item-content {
|
||||
padding: @descriptions-middle-padding;
|
||||
}
|
||||
}
|
||||
&.bordered.small {
|
||||
.@{descriptions-prefix-cls}-item-label,
|
||||
.@{descriptions-prefix-cls}-item-content {
|
||||
padding: @descriptions-small-padding;
|
||||
}
|
||||
}
|
||||
&.bordered {
|
||||
.@{descriptions-prefix-cls}-view {
|
||||
border: 1px solid @border-color-split;
|
||||
}
|
||||
.@{descriptions-prefix-cls}-item-label,
|
||||
.@{descriptions-prefix-cls}-item-content {
|
||||
border-right: 1px solid @border-color-split;
|
||||
}
|
||||
|
||||
.@{descriptions-prefix-cls}-item-label:last-child,
|
||||
.@{descriptions-prefix-cls}-item-content:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.@{descriptions-prefix-cls}-row {
|
||||
border-bottom: 1px solid @border-color-split;
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.@{descriptions-prefix-cls}-item-label {
|
||||
background-color: #fafafa;
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2
components/descriptions/style/index.tsx
Normal file
2
components/descriptions/style/index.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
@ -7,8 +7,8 @@ jest.mock('enquire.js', () => {
|
||||
let unmatchFun;
|
||||
return {
|
||||
unregister: jest.fn(),
|
||||
register: (meidia, options) => {
|
||||
if (meidia === '(max-width: 575px)') {
|
||||
register: (media, options) => {
|
||||
if (media === '(max-width: 575px)') {
|
||||
that = this;
|
||||
options.match.call(that);
|
||||
unmatchFun = options.unmatch;
|
||||
@ -31,23 +31,10 @@ describe('Grid', () => {
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should work correct when gutter is object', () => {
|
||||
// eslint-disable-next-line global-require
|
||||
const enquire = require('enquire.js');
|
||||
const wrapper = mount(<Row gutter={{ xs: 20 }} />);
|
||||
expect(wrapper.find('div').prop('style')).toEqual({
|
||||
marginLeft: -10,
|
||||
marginRight: -10,
|
||||
});
|
||||
enquire.callunmatch();
|
||||
expect(
|
||||
wrapper
|
||||
.update()
|
||||
.find('div')
|
||||
.prop('style'),
|
||||
).toEqual(undefined);
|
||||
it('when typeof getGutter is object', () => {
|
||||
const wrapper = mount(<Row gutter={{ xs: 8, sm: 16, md: 24 }} />);
|
||||
expect(wrapper.instance().getGutter()).toBe(8);
|
||||
wrapper.unmount();
|
||||
expect(enquire.unregister).toHaveBeenCalledTimes(6);
|
||||
});
|
||||
|
||||
it('renders wrapped Col correctly', () => {
|
||||
@ -70,8 +57,22 @@ describe('Grid', () => {
|
||||
expect(willUnmount).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('when typeof getGutter is object', () => {
|
||||
const wrapper = mount(<Row gutter={{ xs: 8, sm: 16, md: 24 }} />).instance();
|
||||
expect(wrapper.getGutter()).toBe(8);
|
||||
it('should work correct when gutter is object', () => {
|
||||
// eslint-disable-next-line global-require
|
||||
const enquire = require('enquire.js');
|
||||
const wrapper = mount(<Row gutter={{ xs: 20 }} />);
|
||||
expect(wrapper.find('div').prop('style')).toEqual({
|
||||
marginLeft: -10,
|
||||
marginRight: -10,
|
||||
});
|
||||
enquire.callunmatch();
|
||||
expect(
|
||||
wrapper
|
||||
.update()
|
||||
.find('div')
|
||||
.prop('style'),
|
||||
).toEqual(undefined);
|
||||
wrapper.unmount();
|
||||
expect(enquire.unregister).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -1,29 +1,15 @@
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
|
||||
// matchMedia polyfill for
|
||||
// https://github.com/WickyNilliams/enquire.js/issues/82
|
||||
let enquire: any;
|
||||
if (typeof window !== 'undefined') {
|
||||
const matchMediaPolyfill = (mediaQuery: string) => {
|
||||
return {
|
||||
media: mediaQuery,
|
||||
matches: false,
|
||||
addListener() {},
|
||||
removeListener() {},
|
||||
};
|
||||
};
|
||||
window.matchMedia = window.matchMedia || matchMediaPolyfill;
|
||||
enquire = require('enquire.js');
|
||||
}
|
||||
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import RowContext from './RowContext';
|
||||
import { tuple } from '../_util/type';
|
||||
import ResponsiveObserve, {
|
||||
Breakpoint,
|
||||
BreakpointMap,
|
||||
responsiveArray,
|
||||
} from '../_util/responsiveObserve';
|
||||
|
||||
export type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
|
||||
export type BreakpointMap = Partial<Record<Breakpoint, string>>;
|
||||
const RowAligns = tuple('top', 'middle', 'bottom');
|
||||
const RowJustify = tuple('start', 'end', 'center', 'space-around', 'space-between');
|
||||
export interface RowProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
@ -38,17 +24,6 @@ export interface RowState {
|
||||
screens: BreakpointMap;
|
||||
}
|
||||
|
||||
const responsiveArray: Breakpoint[] = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
|
||||
|
||||
const responsiveMap: BreakpointMap = {
|
||||
xs: '(max-width: 575px)',
|
||||
sm: '(min-width: 576px)',
|
||||
md: '(min-width: 768px)',
|
||||
lg: '(min-width: 992px)',
|
||||
xl: '(min-width: 1200px)',
|
||||
xxl: '(min-width: 1600px)',
|
||||
};
|
||||
|
||||
export default class Row extends React.Component<RowProps, RowState> {
|
||||
static defaultProps = {
|
||||
gutter: 0,
|
||||
@ -67,41 +42,16 @@ export default class Row extends React.Component<RowProps, RowState> {
|
||||
state: RowState = {
|
||||
screens: {},
|
||||
};
|
||||
|
||||
token: string;
|
||||
componentDidMount() {
|
||||
Object.keys(responsiveMap).map((screen: Breakpoint) =>
|
||||
enquire.register(responsiveMap[screen], {
|
||||
match: () => {
|
||||
if (typeof this.props.gutter !== 'object') {
|
||||
return;
|
||||
}
|
||||
this.setState(prevState => ({
|
||||
screens: {
|
||||
...prevState.screens,
|
||||
[screen]: true,
|
||||
},
|
||||
}));
|
||||
},
|
||||
unmatch: () => {
|
||||
if (typeof this.props.gutter !== 'object') {
|
||||
return;
|
||||
}
|
||||
this.setState(prevState => ({
|
||||
screens: {
|
||||
...prevState.screens,
|
||||
[screen]: false,
|
||||
},
|
||||
}));
|
||||
},
|
||||
// Keep a empty destory to avoid triggering unmatch when unregister
|
||||
destroy() {},
|
||||
}),
|
||||
);
|
||||
this.token = ResponsiveObserve.subscribe(screens => {
|
||||
if (typeof this.props.gutter === 'object') {
|
||||
this.setState({ screens });
|
||||
}
|
||||
});
|
||||
}
|
||||
componentWillUnmount() {
|
||||
Object.keys(responsiveMap).map((screen: Breakpoint) =>
|
||||
enquire.unregister(responsiveMap[screen]),
|
||||
);
|
||||
ResponsiveObserve.unsubscribe(this.token);
|
||||
}
|
||||
getGutter(): number | undefined {
|
||||
const { gutter } = this.props;
|
||||
|
@ -53,6 +53,8 @@ export { default as ConfigProvider } from './config-provider';
|
||||
|
||||
export { default as DatePicker } from './date-picker';
|
||||
|
||||
export { default as Descriptions } from './descriptions';
|
||||
|
||||
export { default as Divider } from './divider';
|
||||
|
||||
export { default as Dropdown } from './dropdown';
|
||||
|
@ -21,6 +21,7 @@ Array [
|
||||
"Comment",
|
||||
"ConfigProvider",
|
||||
"DatePicker",
|
||||
"Descriptions",
|
||||
"Divider",
|
||||
"Dropdown",
|
||||
"Drawer",
|
||||
|
Loading…
Reference in New Issue
Block a user