このコミットが含まれているのは:
@@ -124,7 +124,7 @@ npm run preview
|
|||||||
- For arrays, never put whitespace or a line break immediately before `]`.
|
- For arrays, never put whitespace or a line break immediately before `]`.
|
||||||
- Keep the first element on the same line as `[` by default.
|
- Keep the first element on the same line as `[` by default.
|
||||||
- If an array would exceed the line limit, break after `[` and indent
|
- If an array would exceed the line limit, break after `[` and indent
|
||||||
elements by 4 spaces.
|
elements 4 spaces deeper than the statement's base indentation.
|
||||||
|
|
||||||
### Ruby delimiter and wrapping rules
|
### Ruby delimiter and wrapping rules
|
||||||
|
|
||||||
@@ -140,6 +140,12 @@ npm run preview
|
|||||||
99 文字を超えるなら block 形式へ切り替へるか、message 定数化などで縮める。
|
99 文字を超えるなら block 形式へ切り替へるか、message 定数化などで縮める。
|
||||||
- Ruby の method chain や call argument を折り返す際、call-site の `)` を
|
- Ruby の method chain や call argument を折り返す際、call-site の `)` を
|
||||||
block close のやうに独立させない。
|
block close のやうに独立させない。
|
||||||
|
- Ruby の block body は、その基準位置から 2 空白深くする。
|
||||||
|
- Ruby の wrapped expression、method argument、array element、hash pair などの
|
||||||
|
continuation indentation は、その statement の基準位置から 4 空白深くする。
|
||||||
|
- Ruby の continuation indentation を、行頭からの絶対空白数として扱はない。
|
||||||
|
- 例へば class body 内の array は、class body の 2 空白を基準に、更に 4 空白
|
||||||
|
深くするため、結果として行頭から 6 空白になる。
|
||||||
|
|
||||||
Bad:
|
Bad:
|
||||||
|
|
||||||
@@ -225,20 +231,37 @@ records.each {
|
|||||||
formatting as the local reference shape: component and callback bodies use
|
formatting as the local reference shape: component and callback bodies use
|
||||||
2-space block indentation, single-line bodies do not gain unnecessary
|
2-space block indentation, single-line bodies do not gain unnecessary
|
||||||
braces, wrapped expressions use 4-space continuation indentation, multi-
|
braces, wrapped expressions use 4-space continuation indentation, multi-
|
||||||
stage ternaries use explicit parentheses, and only complete leading runs
|
stage ternaries use explicit parentheses, and TypeScript / TSX indentation
|
||||||
of 8 spaces are compressed to tabs.
|
and alignment whitespace compress every complete run of 8 spaces to tabs.
|
||||||
- In TypeScript and TSX only, tabs are for leading 8-column compression only.
|
|
||||||
- A tab does not represent one indentation level.
|
- A tab does not represent one indentation level.
|
||||||
- Do not replace 2-space or 4-space indentation with tabs.
|
- In TypeScript and TSX, first determine visible indentation using 2-space
|
||||||
- First determine visible indentation using 2-space block indentation and
|
block indentation and 4-space continuation indentation, then compress every
|
||||||
4-space continuation indentation, then compress only complete leading runs
|
complete run of 8 spaces used for indentation or column alignment to tabs.
|
||||||
of 8 spaces into tabs.
|
- In TypeScript and TSX, 8-space compression is mandatory, not optional.
|
||||||
- Tabs are only for leading indentation, never for spaces after non-space
|
- In TypeScript and TSX, this applies both to leading indentation and to
|
||||||
text.
|
alignment whitespace after non-space text, such as aligned inline type
|
||||||
- Keep residual leading 2, 4, or 6 spaces after any tab compression.
|
columns.
|
||||||
|
- In TypeScript and TSX, keep residual 2, 4, or 6 spaces after each tab
|
||||||
|
compression.
|
||||||
|
- In TypeScript and TSX, do not treat a tab as one logical indentation level.
|
||||||
|
- In TypeScript and TSX, do not alter string literals, template-literal
|
||||||
|
contents, regular expressions, or user-facing text merely to apply tab
|
||||||
|
compression.
|
||||||
- Examples: 2 columns = 2 spaces, 4 columns = 4 spaces, 6 columns = 6
|
- Examples: 2 columns = 2 spaces, 4 columns = 4 spaces, 6 columns = 6
|
||||||
spaces, 8 columns = 1 tab, 10 columns = 1 tab + 2 spaces, 12 columns = 1
|
spaces, 8 columns = 1 tab, 10 columns = 1 tab + 2 spaces, 12 columns = 1
|
||||||
tab + 4 spaces, 16 columns = 2 tabs.
|
tab + 4 spaces, 14 columns = 1 tab + 6 spaces, 16 columns = 2 tabs.
|
||||||
|
- When TypeScript or TSX code already uses column alignment, apply the same
|
||||||
|
8-space compression rule to that alignment whitespace.
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type Props = {
|
||||||
|
row: PostImportRow
|
||||||
|
displayNumber?: number
|
||||||
|
onEdit?: () => void
|
||||||
|
onRetry?: () => void
|
||||||
|
onToggleSkip?: (checked: boolean) => void }
|
||||||
|
```
|
||||||
- TypeScript and TSX imports may stay on one line if they remain within the
|
- TypeScript and TSX imports may stay on one line if they remain within the
|
||||||
line limit; do not expand short type-only imports mechanically.
|
line limit; do not expand short type-only imports mechanically.
|
||||||
- Keep runtime value imports and type imports in separate declarations.
|
- Keep runtime value imports and type imports in separate declarations.
|
||||||
@@ -598,10 +621,10 @@ and layout reuse, follow `frontend/AGENTS.md`.
|
|||||||
beginning of a line.
|
beginning of a line.
|
||||||
- The TSX-specific self-review must confirm JSX closing markers and closing
|
- The TSX-specific self-review must confirm JSX closing markers and closing
|
||||||
parentheses keep the surrounding compact style.
|
parentheses keep the surrounding compact style.
|
||||||
- The TypeScript/TSX self-review must confirm leading block indentation uses
|
- The TypeScript/TSX self-review must confirm block indentation uses 2 spaces
|
||||||
2 spaces per level, wrapped continuations use the repository's 4-space
|
per level, wrapped continuations use the repository's 4-space continuation
|
||||||
continuation alignment, and complete leading runs of 8 spaces may be
|
alignment, and every complete run of 8 spaces used for indentation or
|
||||||
compressed to tabs.
|
alignment has been compressed to tabs.
|
||||||
- Prefer `const` arrow functions for TypeScript/TSX component and helper declarations.
|
- Prefer `const` arrow functions for TypeScript/TSX component and helper declarations.
|
||||||
- Put two blank lines before and after top-level `const` function
|
- Put two blank lines before and after top-level `const` function
|
||||||
declarations, unless imports, exports, or file boundaries make that awkward.
|
declarations, unless imports, exports, or file boundaries make that awkward.
|
||||||
@@ -614,9 +637,18 @@ and layout reuse, follow `frontend/AGENTS.md`.
|
|||||||
- Indent the block body 2 spaces deeper than the keyword and opening brace.
|
- Indent the block body 2 spaces deeper than the keyword and opening brace.
|
||||||
- Put the closing `}` on its own line at the same indentation as the keyword.
|
- Put the closing `}` on its own line at the same indentation as the keyword.
|
||||||
- Do not write `try {`, `catch {`, or `finally {`.
|
- Do not write `try {`, `catch {`, or `finally {`.
|
||||||
- In TypeScript and TSX, convert every complete leading run of 8 spaces to a
|
- In TypeScript and TSX, convert every complete run of 8 spaces used for
|
||||||
tab character.
|
indentation or alignment to a tab character.
|
||||||
- A leading tab is exactly equivalent to 8 leading spaces.
|
- In TypeScript and TSX, a tab is exactly equivalent to 8 columns, whether it
|
||||||
|
appears at the beginning of a line or in alignment whitespace after
|
||||||
|
non-space text.
|
||||||
|
- In TSX, JSX nesting uses 2-space block indentation and wrapped JSX
|
||||||
|
attributes use 4-space continuation indentation; after visible columns are
|
||||||
|
determined, compress every complete run of 8 spaces in the resulting
|
||||||
|
indentation or alignment to tabs.
|
||||||
|
- In TSX, do not leave JSX subtree indentation at 8, 10, 12, 14, or 16
|
||||||
|
columns as spaces alone; convert each complete run of 8 spaces to tabs and
|
||||||
|
keep only the residual 2, 4, or 6 spaces.
|
||||||
- In TypeScript and TSX function declarations, including `const` arrow
|
- In TypeScript and TSX function declarations, including `const` arrow
|
||||||
function declarations, classify the parameter list before placing the closing
|
function declarations, classify the parameter list before placing the closing
|
||||||
`)`.
|
`)`.
|
||||||
@@ -1259,9 +1291,9 @@ to `.ts` and `.tsx`:
|
|||||||
7. JSX `>` and `/>` stay with the final prop unless nearby code proves
|
7. JSX `>` and `/>` stay with the final prop unless nearby code proves
|
||||||
otherwise.
|
otherwise.
|
||||||
8. JSX closing parentheses keep the compact local style.
|
8. JSX closing parentheses keep the compact local style.
|
||||||
9. Leading block indentation uses 2 spaces per level, wrapped continuations
|
9. Block indentation uses 2 spaces per level, wrapped continuations use the
|
||||||
use the repository's 4-space continuation alignment, and complete leading
|
repository's 4-space continuation alignment, and every complete run of 8
|
||||||
runs of 8 spaces may be compressed to tabs.
|
spaces used for indentation or alignment has been compressed to tabs.
|
||||||
10. No line has trailing whitespace.
|
10. No line has trailing whitespace.
|
||||||
|
|
||||||
Preferred:
|
Preferred:
|
||||||
|
|||||||
@@ -138,7 +138,8 @@ class Post < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.section_literal section
|
def self.section_literal section
|
||||||
"[#{ Post.ms_to_time(section.begin_ms) }-#{ section.end_ms ? Post.ms_to_time(section.end_ms) : '' }]"
|
"[#{ Post.ms_to_time(section.begin_ms) }-"\
|
||||||
|
"#{ section.end_ms ? Post.ms_to_time(section.end_ms) : '' }]"
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.ms_to_time ms
|
def self.ms_to_time ms
|
||||||
@@ -323,7 +324,9 @@ class Post < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.external_svg_reference?(attribute_name, attribute_value)
|
def self.external_svg_reference?(attribute_name, attribute_value)
|
||||||
return external_svg_url?(attribute_value) if ['href', 'xlink:href', 'src'].include?(attribute_name)
|
if ['href', 'xlink:href', 'src'].include?(attribute_name)
|
||||||
|
return external_svg_url?(attribute_value)
|
||||||
|
end
|
||||||
return style_contains_disallowed_urls?(attribute_value) if attribute_name == 'style'
|
return style_contains_disallowed_urls?(attribute_value) if attribute_name == 'style'
|
||||||
return svg_url_function_disallowed?(attribute_value) if attribute_value.match?(/url\s*\(/i)
|
return svg_url_function_disallowed?(attribute_value) if attribute_value.match?(/url\s*\(/i)
|
||||||
|
|
||||||
@@ -344,7 +347,12 @@ class Post < ApplicationRecord
|
|||||||
|
|
||||||
def self.svg_url_function_disallowed?(value)
|
def self.svg_url_function_disallowed?(value)
|
||||||
value.to_s.scan(/url\s*\(([^)]*)\)/i).flatten.any? do |entry|
|
value.to_s.scan(/url\s*\(([^)]*)\)/i).flatten.any? do |entry|
|
||||||
reference = entry.to_s.strip.delete_prefix("'").delete_prefix('"').delete_suffix("'").delete_suffix('"')
|
reference =
|
||||||
|
entry.to_s.strip
|
||||||
|
.delete_prefix("'")
|
||||||
|
.delete_prefix('"')
|
||||||
|
.delete_suffix("'")
|
||||||
|
.delete_suffix('"')
|
||||||
reference.present? && !(reference.start_with?('#'))
|
reference.present? && !(reference.start_with?('#'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -433,7 +441,12 @@ class Post < ApplicationRecord
|
|||||||
|
|
||||||
match =
|
match =
|
||||||
value.match(
|
value.match(
|
||||||
/\A(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(Z|[+-]\d{2}:?\d{2})?\z/)
|
/
|
||||||
|
\A(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})
|
||||||
|
(?::(\d{2})(?:\.(\d+))?)?
|
||||||
|
(Z|[+-]\d{2}:?\d{2})?
|
||||||
|
\z
|
||||||
|
/x)
|
||||||
return nil if match.nil?
|
return nil if match.nil?
|
||||||
|
|
||||||
year = match[1].to_i
|
year = match[1].to_i
|
||||||
|
|||||||
@@ -85,7 +85,9 @@ class PostCreatePlan
|
|||||||
end_raw: match[3],
|
end_raw: match[3],
|
||||||
tag_name: name)
|
tag_name: name)
|
||||||
end
|
end
|
||||||
raise Tag::SectionLiteralParseError.new(raw_name, raw_name) if name.include?('[') || name.include?(']')
|
if name.include?('[') || name.include?(']')
|
||||||
|
raise Tag::SectionLiteralParseError.new(raw_name, raw_name)
|
||||||
|
end
|
||||||
|
|
||||||
[resolved_tag_name(name), category&.to_sym, sections]
|
[resolved_tag_name(name), category&.to_sym, sections]
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns posts with tag name in JSON" do
|
it 'returns posts with tag name in JSON' do
|
||||||
get "/posts"
|
get "/posts"
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
@@ -161,9 +161,9 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when q is provided" do
|
context 'when q is provided' do
|
||||||
it "filters posts by q (hit case)" do
|
it 'filters posts by q (hit case)' do
|
||||||
get "/posts", params: { tags: "spec_tag" }
|
get '/posts', params: { tags: 'spec_tag' }
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
posts = json.fetch('posts')
|
posts = json.fetch('posts')
|
||||||
@@ -181,8 +181,8 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "filters posts by q (hit case by alias)" do
|
it 'filters posts by q (hit case by alias)' do
|
||||||
get "/posts", params: { tags: "manko" }
|
get '/posts', params: { tags: 'manko' }
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
posts = json.fetch('posts')
|
posts = json.fetch('posts')
|
||||||
@@ -200,7 +200,7 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns empty posts when nothing matches" do
|
it 'returns empty posts when nothing matches' do
|
||||||
get "/posts", params: { tags: "no_such_keyword_12345" }
|
get "/posts", params: { tags: "no_such_keyword_12345" }
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
@@ -1161,7 +1161,7 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when nico tag already exists in tags" do
|
context 'when nico tag already exists in tags' do
|
||||||
before do
|
before do
|
||||||
Tag.find_undiscard_or_create_by!(
|
Tag.find_undiscard_or_create_by!(
|
||||||
tag_name: TagName.find_undiscard_or_create_by!(name: 'nico:nico_tag'),
|
tag_name: TagName.find_undiscard_or_create_by!(name: 'nico:nico_tag'),
|
||||||
@@ -1455,7 +1455,7 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when nico tag already exists in tags" do
|
context 'when nico tag already exists in tags' do
|
||||||
before do
|
before do
|
||||||
Tag.find_undiscard_or_create_by!(
|
Tag.find_undiscard_or_create_by!(
|
||||||
tag_name: TagName.find_undiscard_or_create_by!(name: 'nico:nico_tag'),
|
tag_name: TagName.find_undiscard_or_create_by!(name: 'nico:nico_tag'),
|
||||||
@@ -1712,7 +1712,8 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
expect(post_record.reload.title).to eq('updated by other user')
|
expect(post_record.reload.title).to eq('updated by other user')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns 409 with mergeable true when stale tag changes do not conflict but merge is not requested' do
|
it 'returns 409 with mergeable true when stale tag changes'\
|
||||||
|
' do not conflict but merge is not requested' do
|
||||||
sign_in_as(member)
|
sign_in_as(member)
|
||||||
|
|
||||||
base_version = create_post_version_for!(post_record.reload)
|
base_version = create_post_version_for!(post_record.reload)
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ RSpec.describe "nico:sync" do
|
|||||||
NicoTagRelation.create!(nico_tag_id: nico_tag.id, tag_id: tag.id)
|
NicoTagRelation.create!(nico_tag_id: nico_tag.id, tag_id: tag.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "既存 post を見つけて、nico tag と linked tag を追加し、差分が出たら bot を付ける" do
|
it '既存 post を見つけて、nico tag と linked tag を追加し、差分が出たら bot を付ける' do
|
||||||
# 既存 post(正規表現で拾われるURL)
|
# 既存 post(正規表現で拾われるURL)
|
||||||
post = Post.create!(
|
post = Post.create!(
|
||||||
title: "old",
|
title: 'old',
|
||||||
url: "https://www.nicovideo.jp/watch/sm9",
|
url: 'https://www.nicovideo.jp/watch/sm9',
|
||||||
uploaded_user: nil
|
uploaded_user: nil
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -108,10 +108,10 @@ RSpec.describe "nico:sync" do
|
|||||||
expect(calls).to eq(2)
|
expect(calls).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "既存 post にあった古い nico tag は active から外され、履歴として discard される" do
|
it '既存 post にあった古い nico tag は active から外され、履歴として discard される' do
|
||||||
post = Post.create!(
|
post = Post.create!(
|
||||||
title: "old",
|
title: 'old',
|
||||||
url: "https://www.nicovideo.jp/watch/sm9",
|
url: 'https://www.nicovideo.jp/watch/sm9',
|
||||||
uploaded_user: nil
|
uploaded_user: nil
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,9 @@ describe ('PostEditForm', () => {
|
|||||||
.toHaveValue ('180.5')
|
.toHaveValue ('180.5')
|
||||||
})
|
})
|
||||||
|
|
||||||
it ('shows deduplicated original-created endpoint errors on the shared datetime field', async () => {
|
it (
|
||||||
|
'shows deduplicated original-created endpoint errors on the shared datetime field',
|
||||||
|
async () => {
|
||||||
const post = buildPost ()
|
const post = buildPost ()
|
||||||
api.isApiError.mockReturnValue (true)
|
api.isApiError.mockReturnValue (true)
|
||||||
postsApi.updatePost.mockRejectedValueOnce ({
|
postsApi.updatePost.mockRejectedValueOnce ({
|
||||||
@@ -114,5 +116,5 @@ describe ('PostEditForm', () => {
|
|||||||
expect (await screen.findByText ('日時を確認してください.')).toBeInTheDocument ()
|
expect (await screen.findByText ('日時を確認してください.')).toBeInTheDocument ()
|
||||||
expect (screen.getByText ('終了を確認してください.')).toBeInTheDocument ()
|
expect (screen.getByText ('終了を確認してください.')).toBeInTheDocument ()
|
||||||
expect (screen.getAllByText ('日時を確認してください.')).toHaveLength (1)
|
expect (screen.getAllByText ('日時を確認してください.')).toHaveLength (1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -75,8 +75,6 @@ const TagLink: FC<Props> = ({ tag,
|
|||||||
const spanClass = 'tag-link-colour'
|
const spanClass = 'tag-link-colour'
|
||||||
const linkClass = 'tag-link-colour tag-link-hover-colour'
|
const linkClass = 'tag-link-colour tag-link-hover-colour'
|
||||||
const textClass = 'group min-w-0 max-w-full overflow-hidden align-bottom'
|
const textClass = 'group min-w-0 max-w-full overflow-hidden align-bottom'
|
||||||
const rootClass =
|
|
||||||
'inline-flex min-w-0 max-w-full flex-nowrap items-stretch align-baseline gap-x-1 md:items-baseline'
|
|
||||||
const markerWrapClass = 'shrink-0 self-start md:self-auto'
|
const markerWrapClass = 'shrink-0 self-start md:self-auto'
|
||||||
const countClass = 'shrink-0 self-end md:self-auto'
|
const countClass = 'shrink-0 self-end md:self-auto'
|
||||||
const matchedAlias = isFullTag (tag) ? tag.matchedAlias : null
|
const matchedAlias = isFullTag (tag) ? tag.matchedAlias : null
|
||||||
@@ -84,7 +82,10 @@ const TagLink: FC<Props> = ({ tag,
|
|||||||
?? (matchedAlias == null ? tag.name : `${ matchedAlias } → ${ tag.name }`)
|
?? (matchedAlias == null ? tag.name : `${ matchedAlias } → ${ tag.name }`)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={rootClass}>
|
<span
|
||||||
|
className={cn (
|
||||||
|
'inline-flex min-w-0 max-w-full flex-nowrap items-stretch align-baseline',
|
||||||
|
'gap-x-1 md:items-baseline')}>
|
||||||
{(linkFlg && withWiki && isFullTag (tag)) && (
|
{(linkFlg && withWiki && isFullTag (tag)) && (
|
||||||
<span className={markerWrapClass}>
|
<span className={markerWrapClass}>
|
||||||
{(tag.materialId != null || tag.hasWiki || tag.hasDeerjikists)
|
{(tag.materialId != null || tag.hasWiki || tag.hasDeerjikists)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client"
|
'use client'
|
||||||
|
|
||||||
import * as React from "react"
|
import * as DialogPrimitive from '@radix-ui/react-dialog'
|
||||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
import { X } from 'lucide-react'
|
||||||
import { X } from "lucide-react"
|
import * as React from 'react'
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const Dialog = DialogPrimitive.Root
|
const Dialog = DialogPrimitive.Root
|
||||||
|
|
||||||
@@ -15,111 +15,109 @@ const DialogPortal = DialogPrimitive.Portal
|
|||||||
const DialogClose = DialogPrimitive.Close
|
const DialogClose = DialogPrimitive.Close
|
||||||
|
|
||||||
const DialogOverlay = React.forwardRef<
|
const DialogOverlay = React.forwardRef<
|
||||||
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||||
>(({ className, ...props }, ref) => (
|
>(
|
||||||
<DialogPrimitive.Overlay
|
({ className, ...props }, ref) => (
|
||||||
ref={ref}
|
<DialogPrimitive.Overlay
|
||||||
className={cn(
|
ref={ref}
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
className={cn (
|
||||||
className)}
|
'fixed inset-0 z-50 bg-black/80',
|
||||||
{...props}
|
'data-[state=open]:animate-in data-[state=closed]:animate-out',
|
||||||
/>))
|
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||||
|
className)}
|
||||||
|
{...props}/>))
|
||||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
|
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
|
||||||
|
|
||||||
const DialogContent = React.forwardRef<
|
const DialogContent = React.forwardRef<
|
||||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||||
>(({ className, children, ...props }, ref) => (
|
>(
|
||||||
<DialogPortal>
|
({ className, children, ...props }, ref) => (
|
||||||
<DialogOverlay />
|
<DialogPortal>
|
||||||
<DialogPrimitive.Content
|
<DialogOverlay />
|
||||||
ref={ref}
|
<DialogPrimitive.Content
|
||||||
className={cn (
|
ref={ref}
|
||||||
'fixed left-[50%] top-[50%] z-50 grid w-[calc(100%-2rem)] max-w-lg',
|
className={cn (
|
||||||
'translate-x-[-50%] translate-y-[-50%]',
|
'fixed left-[50%] top-[50%] z-50 grid w-[calc(100%-2rem)] max-w-lg',
|
||||||
'gap-5 rounded-2xl border border-border',
|
'translate-x-[-50%] translate-y-[-50%]',
|
||||||
'bg-background p-6 text-foreground shadow-2xl',
|
'gap-5 rounded-2xl border border-border',
|
||||||
'duration-200',
|
'bg-background p-6 text-foreground shadow-2xl',
|
||||||
'data-[state=open]:animate-in data-[state=closed]:animate-out',
|
'duration-200',
|
||||||
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
'data-[state=open]:animate-in data-[state=closed]:animate-out',
|
||||||
'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
|
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||||
className)}
|
'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
|
||||||
{...props}
|
className)}
|
||||||
>
|
{...props}>
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
<DialogPrimitive.Close
|
<DialogPrimitive.Close
|
||||||
className={cn (
|
className={cn (
|
||||||
'absolute left-4 top-4 rounded-full p-1',
|
'absolute left-4 top-4 rounded-full p-1',
|
||||||
'text-slate-500 transition-colors',
|
'text-slate-500 transition-colors',
|
||||||
'hover:bg-slate-200 hover:text-slate-900',
|
'hover:bg-slate-200 hover:text-slate-900',
|
||||||
'dark:text-slate-400 dark:hover:bg-slate-700 dark:hover:text-slate-50',
|
'dark:text-slate-400 dark:hover:bg-slate-700 dark:hover:text-slate-50',
|
||||||
'focus:outline-none focus:ring-2 focus:ring-slate-400')}>
|
'focus:outline-none focus:ring-2 focus:ring-slate-400')}>
|
||||||
<X className="h-4 w-4"/>
|
<X className="h-4 w-4"/>
|
||||||
<span className="sr-only">閉ぢる</span>
|
<span className="sr-only">閉ぢる</span>
|
||||||
</DialogPrimitive.Close>
|
</DialogPrimitive.Close>
|
||||||
</DialogPrimitive.Content>
|
</DialogPrimitive.Content>
|
||||||
</DialogPortal>))
|
</DialogPortal>))
|
||||||
DialogContent.displayName = DialogPrimitive.Content.displayName
|
DialogContent.displayName = DialogPrimitive.Content.displayName
|
||||||
|
|
||||||
const DialogHeader = ({
|
const DialogHeader = (
|
||||||
className,
|
{ className, ...props }: React.HTMLAttributes<HTMLDivElement>,
|
||||||
...props
|
) => (
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
<div
|
||||||
<div
|
className={cn (
|
||||||
className={cn(
|
'flex flex-col space-y-1.5 text-center md:text-left',
|
||||||
"flex flex-col space-y-1.5 text-center md:text-left",
|
className)}
|
||||||
className)}
|
{...props}/>)
|
||||||
{...props}
|
DialogHeader.displayName = 'DialogHeader'
|
||||||
/>)
|
|
||||||
DialogHeader.displayName = "DialogHeader"
|
|
||||||
|
|
||||||
const DialogFooter = ({
|
const DialogFooter = (
|
||||||
className,
|
{ className, ...props }: React.HTMLAttributes<HTMLDivElement>,
|
||||||
...props
|
) => (
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
<div
|
||||||
<div
|
className={cn (
|
||||||
className={cn(
|
'flex flex-col-reverse md:flex-row md:justify-end md:space-x-2',
|
||||||
"flex flex-col-reverse md:flex-row md:justify-end md:space-x-2",
|
className)}
|
||||||
className)}
|
{...props}/>)
|
||||||
{...props}
|
DialogFooter.displayName = 'DialogFooter'
|
||||||
/>)
|
|
||||||
DialogFooter.displayName = "DialogFooter"
|
|
||||||
|
|
||||||
const DialogTitle = React.forwardRef<
|
const DialogTitle = React.forwardRef<
|
||||||
React.ElementRef<typeof DialogPrimitive.Title>,
|
React.ElementRef<typeof DialogPrimitive.Title>,
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
||||||
>(({ className, ...props }, ref) => (
|
>(
|
||||||
<DialogPrimitive.Title
|
({ className, ...props }, ref) => (
|
||||||
ref={ref}
|
<DialogPrimitive.Title
|
||||||
className={cn(
|
ref={ref}
|
||||||
"text-lg font-semibold leading-none tracking-tight",
|
className={cn (
|
||||||
className)}
|
'text-lg font-semibold leading-none tracking-tight',
|
||||||
{...props}
|
className)}
|
||||||
/>))
|
{...props}/>))
|
||||||
DialogTitle.displayName = DialogPrimitive.Title.displayName
|
DialogTitle.displayName = DialogPrimitive.Title.displayName
|
||||||
|
|
||||||
const DialogDescription = React.forwardRef<
|
const DialogDescription = React.forwardRef<
|
||||||
React.ElementRef<typeof DialogPrimitive.Description>,
|
React.ElementRef<typeof DialogPrimitive.Description>,
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
||||||
>(({ className, ...props }, ref) => (
|
>(
|
||||||
<DialogPrimitive.Description
|
({ className, ...props }, ref) => (
|
||||||
ref={ref}
|
<DialogPrimitive.Description
|
||||||
className={cn("text-sm text-muted-foreground", className)}
|
ref={ref}
|
||||||
{...props}
|
className={cn ('text-sm text-muted-foreground', className)}
|
||||||
/>))
|
{...props}/>))
|
||||||
DialogDescription.displayName = DialogPrimitive.Description.displayName
|
DialogDescription.displayName = DialogPrimitive.Description.displayName
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogPortal,
|
DialogPortal,
|
||||||
DialogOverlay,
|
DialogOverlay,
|
||||||
DialogClose,
|
DialogClose,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,7 +262,9 @@ describe ('TheatreDetailPage', () => {
|
|||||||
expect (postEmbed.seek).not.toHaveBeenCalledWith (0)
|
expect (postEmbed.seek).not.toHaveBeenCalledWith (0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it ('shows child tags from the post tag tree in both vertical and horizontal layouts', async () => {
|
it (
|
||||||
|
'shows child tags from the post tag tree in both vertical and horizontal layouts',
|
||||||
|
async () => {
|
||||||
const childTag = buildTag ({ id: 12, name: '子タグ', category: 'general' })
|
const childTag = buildTag ({ id: 12, name: '子タグ', category: 'general' })
|
||||||
const parentTag = buildTag ({
|
const parentTag = buildTag ({
|
||||||
id: 11,
|
id: 11,
|
||||||
@@ -307,7 +309,7 @@ describe ('TheatreDetailPage', () => {
|
|||||||
expect (within (tagSection ()).getAllByRole ('link', { name: '子タグ' }))
|
expect (within (tagSection ()).getAllByRole ('link', { name: '子タグ' }))
|
||||||
.toHaveLength (1)
|
.toHaveLength (1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it ('does not advance host post while video length is unknown', async () => {
|
it ('does not advance host post while video length is unknown', async () => {
|
||||||
api.apiPut.mockImplementation ((path: string) => {
|
api.apiPut.mockImplementation ((path: string) => {
|
||||||
|
|||||||
新しい課題から参照
ユーザをブロックする