@operato/menu 10.6.0 → 10.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.js +4 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/menu-active.d.ts +89 -0
- package/dist/src/menu-active.js +115 -0
- package/dist/src/menu-active.js.map +1 -0
- package/dist/src/menu-mode.d.ts +98 -0
- package/dist/src/menu-mode.js +100 -0
- package/dist/src/menu-mode.js.map +1 -0
- package/dist/src/menu-portrait-styles.d.ts +27 -0
- package/dist/src/menu-portrait-styles.js +516 -92
- package/dist/src/menu-portrait-styles.js.map +1 -1
- package/dist/src/menu-privilege.d.ts +34 -0
- package/dist/src/menu-privilege.js +22 -0
- package/dist/src/menu-privilege.js.map +1 -0
- package/dist/src/ox-menu-part.d.ts +103 -0
- package/dist/src/ox-menu-part.js +224 -23
- package/dist/src/ox-menu-part.js.map +1 -1
- package/dist/src/ox-menu-portrait.d.ts +158 -3
- package/dist/src/ox-menu-portrait.js +511 -13
- package/dist/src/ox-menu-portrait.js.map +1 -1
- package/dist/src/types.d.ts +38 -1
- package/dist/src/types.js.map +1 -1
- package/dist/test/menu-active.test.d.ts +1 -0
- package/dist/test/menu-active.test.js +131 -0
- package/dist/test/menu-active.test.js.map +1 -0
- package/dist/test/menu-mode.test.d.ts +1 -0
- package/dist/test/menu-mode.test.js +108 -0
- package/dist/test/menu-mode.test.js.map +1 -0
- package/dist/test/menu-privilege.test.d.ts +1 -0
- package/dist/test/menu-privilege.test.js +94 -0
- package/dist/test/menu-privilege.test.js.map +1 -0
- package/dist/test/ox-menu-portrait.test.d.ts +1 -0
- package/dist/test/ox-menu-portrait.test.js +339 -0
- package/dist/test/ox-menu-portrait.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/test/menu-active.test.ts +162 -0
- package/test/menu-mode.test.ts +135 -0
- package/test/menu-privilege.test.ts +138 -0
- package/test/ox-menu-portrait.test.ts +431 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { expect } from '@open-wc/testing'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
AUTO_COLLAPSE_UNDER,
|
|
5
|
+
childrenOpenAs,
|
|
6
|
+
menuMode,
|
|
7
|
+
shouldAutoCollapse,
|
|
8
|
+
showsLabels,
|
|
9
|
+
topNavigates
|
|
10
|
+
} from '../src/menu-mode.js'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 왼쪽 메뉴의 **모양 표**.
|
|
14
|
+
*
|
|
15
|
+
* 이 표가 깨진 적이 네 번이다 — 합계 배지 감추기 · 시스템 자리 · 팝업 꾸밈 · 서랍 판정. 넷 다
|
|
16
|
+
* 화면을 눈으로 보고서야 알았고, 넷 다 「이 모양에서는 어떻게 되나」를 그때그때 판단해서 났다.
|
|
17
|
+
*
|
|
18
|
+
* 여기서 표 전체를 못박는다. 규칙을 하나 고치면 다른 칸이 함께 빨개진다.
|
|
19
|
+
*/
|
|
20
|
+
describe('메뉴 모양', () => {
|
|
21
|
+
describe('세 플래그에서 모양 하나를 정한다', () => {
|
|
22
|
+
it('아무것도 켜지 않으면 기존 메뉴다', () => {
|
|
23
|
+
expect(menuMode({})).to.equal('list')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('레일만 켜면 펼친 레일이다', () => {
|
|
27
|
+
expect(menuMode({ rail: true })).to.equal('rail')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('레일에 접힘을 더하면 접힌 레일이다', () => {
|
|
31
|
+
expect(menuMode({ rail: true, collapsed: true })).to.equal('rail-collapsed')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('레일 없이 접힘만 켜도 기존 메뉴다 — 접을 대상이 없다', () => {
|
|
35
|
+
expect(menuMode({ collapsed: true })).to.equal('list')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('서랍이 가장 세다 — 레일과 접힘을 함께 켜도 서랍이다', () => {
|
|
39
|
+
expect(menuMode({ drawer: true })).to.equal('drawer')
|
|
40
|
+
expect(menuMode({ drawer: true, rail: true })).to.equal('drawer')
|
|
41
|
+
expect(menuMode({ drawer: true, rail: true, collapsed: true })).to.equal('drawer')
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('하위를 어디에 여나', () => {
|
|
46
|
+
it('접힌 레일에서만 팝업이다 — 56px 안에 들여쓴 목록이 안 들어간다', () => {
|
|
47
|
+
expect(childrenOpenAs('rail-collapsed')).to.equal('popup')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('나머지 셋은 그 자리에서 열린다', () => {
|
|
51
|
+
expect(childrenOpenAs('list')).to.equal('inline')
|
|
52
|
+
expect(childrenOpenAs('rail')).to.equal('inline')
|
|
53
|
+
expect(childrenOpenAs('drawer')).to.equal('inline')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('서랍은 팝업이 아니다 — 화면 위에 뜬 서랍 옆에 고정 팝업이 설 자리가 없다', () => {
|
|
57
|
+
expect(childrenOpenAs(menuMode({ drawer: true }))).to.equal('inline')
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
describe('최상위를 누르면 이동하나', () => {
|
|
62
|
+
it('늘 보이는 메뉴에서는 이동한다 — 누르면 어디든 도착한다', () => {
|
|
63
|
+
expect(topNavigates('list')).to.be.true
|
|
64
|
+
expect(topNavigates('rail')).to.be.true
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('서랍에서는 이동하지 않는다 — 이동하면 서랍이 닫혀 나머지 하위를 볼 기회가 없다', () => {
|
|
68
|
+
expect(topNavigates('drawer')).to.be.false
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('접힌 레일에서도 이동하지 않는다 — 팝업을 열어 고르게 한다', () => {
|
|
72
|
+
expect(topNavigates('rail-collapsed')).to.be.false
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe('이름이 보이나', () => {
|
|
77
|
+
it('접힌 레일만 아이콘뿐이다', () => {
|
|
78
|
+
expect(showsLabels('rail-collapsed')).to.be.false
|
|
79
|
+
expect(showsLabels('list')).to.be.true
|
|
80
|
+
expect(showsLabels('rail')).to.be.true
|
|
81
|
+
expect(showsLabels('drawer')).to.be.true
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('좁아지면 스스로 접는다', () => {
|
|
86
|
+
it('기준 폭 아래면 접는다 — 이름이 이름 노릇을 못 한다', () => {
|
|
87
|
+
expect(shouldAutoCollapse(120, { rail: true })).to.be.true
|
|
88
|
+
expect(shouldAutoCollapse(AUTO_COLLAPSE_UNDER, { rail: true })).to.be.true
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('기준 폭보다 넓으면 그대로 둔다', () => {
|
|
92
|
+
expect(shouldAutoCollapse(AUTO_COLLAPSE_UNDER + 1, { rail: true })).to.be.false
|
|
93
|
+
expect(shouldAutoCollapse(208, { rail: true })).to.be.false
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('이미 접혔으면 보지 않는다 — 56px 이 이 판정에 다시 걸려 되풀이된다', () => {
|
|
97
|
+
expect(shouldAutoCollapse(56, { rail: true, collapsed: true })).to.be.false
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('레일이 아니면 접을 것이 없다', () => {
|
|
101
|
+
expect(shouldAutoCollapse(60, {})).to.be.false
|
|
102
|
+
expect(shouldAutoCollapse(60, { rail: false })).to.be.false
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('폭이 0 이면 판단하지 않는다 — 붙는 순간이거나 감춰진 순간이다', () => {
|
|
106
|
+
expect(shouldAutoCollapse(0, { rail: true })).to.be.false
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
describe('표 전체 — 네 모양 × 세 답', () => {
|
|
111
|
+
/**
|
|
112
|
+
* 이 배열이 곧 문서다. 규칙을 고치면 여기도 고쳐야 하고, 고치는 순간 그 판단을 한 번 더
|
|
113
|
+
* 들여다보게 된다.
|
|
114
|
+
*/
|
|
115
|
+
const 표: { 모양: ReturnType<typeof menuMode>; 하위: 'inline' | 'popup'; 이동: boolean; 이름: boolean }[] = [
|
|
116
|
+
{ 모양: 'list', 하위: 'inline', 이동: true, 이름: true },
|
|
117
|
+
{ 모양: 'rail', 하위: 'inline', 이동: true, 이름: true },
|
|
118
|
+
{ 모양: 'rail-collapsed', 하위: 'popup', 이동: false, 이름: false },
|
|
119
|
+
{ 모양: 'drawer', 하위: 'inline', 이동: false, 이름: true }
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
표.forEach(줄 => {
|
|
123
|
+
it(`${줄.모양} — 하위 ${줄.하위} · 이동 ${줄.이동} · 이름 ${줄.이름}`, () => {
|
|
124
|
+
expect(childrenOpenAs(줄.모양)).to.equal(줄.하위)
|
|
125
|
+
expect(topNavigates(줄.모양)).to.equal(줄.이동)
|
|
126
|
+
expect(showsLabels(줄.모양)).to.equal(줄.이름)
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('네 모양을 다 덮는다 — 새 모양이 생기면 이 시험이 먼저 빨개진다', () => {
|
|
131
|
+
const 모양들: ReturnType<typeof menuMode>[] = ['list', 'rail', 'rail-collapsed', 'drawer']
|
|
132
|
+
expect(표.map(줄 => 줄.모양)).to.deep.equal(모양들)
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
})
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { expect } from '@open-wc/testing'
|
|
2
|
+
|
|
3
|
+
import { Menu } from '../src/types.js'
|
|
4
|
+
import { permittedMenus } from '../src/menu-privilege.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 쓸 수 있는 메뉴만 남기는 규칙.
|
|
8
|
+
*
|
|
9
|
+
* 이 판단이 앱마다 있었다. operato-plant 는 「소유자면 조직 묶음을 배열에 넣는다」였고,
|
|
10
|
+
* operato-twin 은 항목마다 `superOnly` · `ownerOnly` 플래그였다. 같은 물음에 답이 둘이었고,
|
|
11
|
+
* 둘 다 **서버의 문과 다른 낱말**이라 문이 열어 주는 사람에게 메뉴가 안 보이는 일이 났다.
|
|
12
|
+
*
|
|
13
|
+
* 규칙을 한 자리에 두고 여기서 못박는다.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** 못 가진 요구 하나 — 이것이 걸리면 그 줄은 사라져야 한다. */
|
|
17
|
+
const 없는권한 = { category: '없는분류', privilege: 'query' }
|
|
18
|
+
const 있는권한 = { category: 'employee', privilege: 'query' }
|
|
19
|
+
|
|
20
|
+
/** 시험용 판정 — 「employee/query 만 가졌다」. */
|
|
21
|
+
const 가진사람: (p: NonNullable<Menu['privilege']>) => boolean = p =>
|
|
22
|
+
p.category === 있는권한.category && p.privilege === 있는권한.privilege
|
|
23
|
+
|
|
24
|
+
const 이름들 = (menus: Menu[]) => menus.map(menu => menu.name)
|
|
25
|
+
|
|
26
|
+
describe('쓸 수 있는 메뉴만 남긴다', () => {
|
|
27
|
+
it('요구가 없는 줄은 그대로 둔다', async () => {
|
|
28
|
+
const 남은것 = await permittedMenus([{ name: 'plain', path: 'a' }], 가진사람)
|
|
29
|
+
|
|
30
|
+
expect(이름들(남은것)).to.deep.equal(['plain'])
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('가진 요구면 남고, 못 가진 요구면 빠진다', async () => {
|
|
34
|
+
const 남은것 = await permittedMenus(
|
|
35
|
+
[
|
|
36
|
+
{ name: 'mine', path: 'a', privilege: 있는권한 },
|
|
37
|
+
{ name: 'theirs', path: 'b', privilege: 없는권한 }
|
|
38
|
+
],
|
|
39
|
+
가진사람
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
expect(이름들(남은것)).to.deep.equal(['mine'])
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('부모가 걸리면 자식까지 뺀다 — 묶음에 못 들어가면 그 안도 갈 수 없다', async () => {
|
|
46
|
+
const 남은것 = await permittedMenus(
|
|
47
|
+
[{ name: 'group', path: 'a', privilege: 없는권한, menus: [{ name: 'kid', path: 'a1' }] }],
|
|
48
|
+
가진사람
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
expect(남은것).to.deep.equal([])
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('자식이 다 빠진 부모도 뺀다 — 눌러도 아무 데도 못 간다', async () => {
|
|
55
|
+
const 남은것 = await permittedMenus(
|
|
56
|
+
[{ name: 'group', path: 'a', menus: [{ name: 'kid', path: 'a1', privilege: 없는권한 }] }],
|
|
57
|
+
가진사람
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
expect(남은것).to.deep.equal([])
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('자식이 하나라도 남으면 부모도 남는다 — 남은 것만 데리고', async () => {
|
|
64
|
+
const 남은것 = await permittedMenus(
|
|
65
|
+
[
|
|
66
|
+
{
|
|
67
|
+
name: 'group',
|
|
68
|
+
path: 'a',
|
|
69
|
+
menus: [
|
|
70
|
+
{ name: 'stay', path: 'a1' },
|
|
71
|
+
{ name: 'go', path: 'a2', privilege: 없는권한 }
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
가진사람
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
expect(이름들(남은것)).to.deep.equal(['group'])
|
|
79
|
+
expect(이름들(남은것[0].menus!)).to.deep.equal(['stay'])
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('원래 자식이 없던 잎은 남는다 — 「자식이 다 빠졌다」로 읽지 않는다', async () => {
|
|
83
|
+
const 남은것 = await permittedMenus([{ name: 'leaf', path: 'a' }], 가진사람)
|
|
84
|
+
|
|
85
|
+
expect(이름들(남은것), '이 갈래가 없으면 잎이 전부 사라진다').to.deep.equal(['leaf'])
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('원래 것을 고치지 않는다 — 다시 거를 때 앞의 결과가 섞이지 않게', async () => {
|
|
89
|
+
const 원래: Menu[] = [
|
|
90
|
+
{
|
|
91
|
+
name: 'group',
|
|
92
|
+
path: 'a',
|
|
93
|
+
menus: [
|
|
94
|
+
{ name: 'stay', path: 'a1' },
|
|
95
|
+
{ name: 'go', path: 'a2', privilege: 없는권한 }
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
await permittedMenus(원래, 가진사람)
|
|
101
|
+
|
|
102
|
+
expect(원래[0].menus, '입력을 고치면 로그인할 때마다 메뉴가 줄어든다').to.have.lengthOf(2)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('판정이 늦게 와도 기다린다 — 프로필은 비동기로 온다', async () => {
|
|
106
|
+
const 느린판정 = async (p: NonNullable<Menu['privilege']>) => {
|
|
107
|
+
await new Promise(resolve => setTimeout(resolve, 5))
|
|
108
|
+
return 가진사람(p)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const 남은것 = await permittedMenus(
|
|
112
|
+
[
|
|
113
|
+
{ name: 'mine', path: 'a', privilege: 있는권한 },
|
|
114
|
+
{ name: 'theirs', path: 'b', privilege: 없는권한 }
|
|
115
|
+
],
|
|
116
|
+
느린판정
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
expect(이름들(남은것)).to.deep.equal(['mine'])
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('여러 층을 내려간다', async () => {
|
|
123
|
+
const 남은것 = await permittedMenus(
|
|
124
|
+
[
|
|
125
|
+
{
|
|
126
|
+
name: 'top',
|
|
127
|
+
menus: [
|
|
128
|
+
{ name: 'mid', menus: [{ name: 'deep', path: 'x', privilege: 없는권한 }] },
|
|
129
|
+
{ name: 'keep', path: 'y' }
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
],
|
|
133
|
+
가진사람
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
expect(이름들(남은것[0].menus!), '깊은 곳이 비면 그 부모도 함께 빠진다').to.deep.equal(['keep'])
|
|
137
|
+
})
|
|
138
|
+
})
|