From 55de7cf9036eeb217c4aca01b305c96c66b09d5b Mon Sep 17 00:00:00 2001 From: Jeff Verkoeyen Date: Sat, 3 Aug 2024 10:46:04 -0400 Subject: [PATCH] Add h1-h6 implementations. Part of https://github.com/jverkoey/slipstream/issues/25. --- .../Documentation.docc/Views/W3C/W3CViews.md | 6 ++++ .../Slipstream/W3C/Elements/Sections/H1.swift | 33 +++++++++++++++++++ .../Slipstream/W3C/Elements/Sections/H2.swift | 33 +++++++++++++++++++ .../Slipstream/W3C/Elements/Sections/H3.swift | 33 +++++++++++++++++++ .../Slipstream/W3C/Elements/Sections/H4.swift | 33 +++++++++++++++++++ .../Slipstream/W3C/Elements/Sections/H5.swift | 33 +++++++++++++++++++ .../Slipstream/W3C/Elements/Sections/H6.swift | 33 +++++++++++++++++++ .../Sites/CatalogSiteTests.swift | 14 ++++++++ .../Views/W3C/HeadingTests.swift | 32 ++++++++++++++++++ 9 files changed, 250 insertions(+) create mode 100644 Sources/Slipstream/W3C/Elements/Sections/H1.swift create mode 100644 Sources/Slipstream/W3C/Elements/Sections/H2.swift create mode 100644 Sources/Slipstream/W3C/Elements/Sections/H3.swift create mode 100644 Sources/Slipstream/W3C/Elements/Sections/H4.swift create mode 100644 Sources/Slipstream/W3C/Elements/Sections/H5.swift create mode 100644 Sources/Slipstream/W3C/Elements/Sections/H6.swift create mode 100644 Tests/SlipstreamTests/Views/W3C/HeadingTests.swift diff --git a/Sources/Slipstream/Documentation.docc/Views/W3C/W3CViews.md b/Sources/Slipstream/Documentation.docc/Views/W3C/W3CViews.md index e76ebcb..69ae14d 100644 --- a/Sources/Slipstream/Documentation.docc/Views/W3C/W3CViews.md +++ b/Sources/Slipstream/Documentation.docc/Views/W3C/W3CViews.md @@ -22,6 +22,12 @@ The complete W3C HTML elements standard can be found [here](https://html.spec.wh ### Sections - ``Body`` +- ``H1`` +- ``H2`` +- ``H3`` +- ``H4`` +- ``H5`` +- ``H6`` ### Grouping content diff --git a/Sources/Slipstream/W3C/Elements/Sections/H1.swift b/Sources/Slipstream/W3C/Elements/Sections/H1.swift new file mode 100644 index 0000000..01c8352 --- /dev/null +++ b/Sources/Slipstream/W3C/Elements/Sections/H1.swift @@ -0,0 +1,33 @@ +/// A view that represents a top-level heading for a section. +/// +/// ```swift +/// struct MySiteContent: View { +/// var body: some View { +/// Body { +/// H1("Hello, world!") +/// } +/// } +/// } +/// ``` +/// +/// - SeeAlso: W3C [`h1-h6`](https://html.spec.whatwg.org/multipage/sections.html#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements) specification. +@available(iOS 17.0, macOS 14.0, *) +public struct H1: W3CElement where Content: View { + @_documentation(visibility: private) + public let tagName: String = "h1" + + @_documentation(visibility: private) + @ViewBuilder public let content: () -> Content + + /// Creates an H1 view. + public init(@ViewBuilder content: @escaping () -> Content) { + self.content = content + } + + /// Creates an H1 view with some static text. + public init(_ text: String) where Content == Text { + self.content = { + Text(text) + } + } +} diff --git a/Sources/Slipstream/W3C/Elements/Sections/H2.swift b/Sources/Slipstream/W3C/Elements/Sections/H2.swift new file mode 100644 index 0000000..7dd5f2c --- /dev/null +++ b/Sources/Slipstream/W3C/Elements/Sections/H2.swift @@ -0,0 +1,33 @@ +/// A view that represents a heading for a section. +/// +/// ```swift +/// struct MySiteContent: View { +/// var body: some View { +/// Body { +/// H2("Hello, world!") +/// } +/// } +/// } +/// ``` +/// +/// - SeeAlso: W3C [`h1-h6`](https://html.spec.whatwg.org/multipage/sections.html#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements) specification. +@available(iOS 17.0, macOS 14.0, *) +public struct H2: W3CElement where Content: View { + @_documentation(visibility: private) + public let tagName: String = "h2" + + @_documentation(visibility: private) + @ViewBuilder public let content: () -> Content + + /// Creates an H2 view. + public init(@ViewBuilder content: @escaping () -> Content) { + self.content = content + } + + /// Creates an H2 view with some static text. + public init(_ text: String) where Content == Text { + self.content = { + Text(text) + } + } +} diff --git a/Sources/Slipstream/W3C/Elements/Sections/H3.swift b/Sources/Slipstream/W3C/Elements/Sections/H3.swift new file mode 100644 index 0000000..88eedd0 --- /dev/null +++ b/Sources/Slipstream/W3C/Elements/Sections/H3.swift @@ -0,0 +1,33 @@ +/// A view that represents a heading for a section. +/// +/// ```swift +/// struct MySiteContent: View { +/// var body: some View { +/// Body { +/// H3("Hello, world!") +/// } +/// } +/// } +/// ``` +/// +/// - SeeAlso: W3C [`h1-h6`](https://html.spec.whatwg.org/multipage/sections.html#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements) specification. +@available(iOS 17.0, macOS 14.0, *) +public struct H3: W3CElement where Content: View { + @_documentation(visibility: private) + public let tagName: String = "h3" + + @_documentation(visibility: private) + @ViewBuilder public let content: () -> Content + + /// Creates an H3 view. + public init(@ViewBuilder content: @escaping () -> Content) { + self.content = content + } + + /// Creates an H3 view with some static text. + public init(_ text: String) where Content == Text { + self.content = { + Text(text) + } + } +} diff --git a/Sources/Slipstream/W3C/Elements/Sections/H4.swift b/Sources/Slipstream/W3C/Elements/Sections/H4.swift new file mode 100644 index 0000000..e42eeee --- /dev/null +++ b/Sources/Slipstream/W3C/Elements/Sections/H4.swift @@ -0,0 +1,33 @@ +/// A view that represents a heading for a section. +/// +/// ```swift +/// struct MySiteContent: View { +/// var body: some View { +/// Body { +/// H4("Hello, world!") +/// } +/// } +/// } +/// ``` +/// +/// - SeeAlso: W3C [`h1-h6`](https://html.spec.whatwg.org/multipage/sections.html#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements) specification. +@available(iOS 17.0, macOS 14.0, *) +public struct H4: W3CElement where Content: View { + @_documentation(visibility: private) + public let tagName: String = "h4" + + @_documentation(visibility: private) + @ViewBuilder public let content: () -> Content + + /// Creates an H4 view. + public init(@ViewBuilder content: @escaping () -> Content) { + self.content = content + } + + /// Creates an H4 view with some static text. + public init(_ text: String) where Content == Text { + self.content = { + Text(text) + } + } +} diff --git a/Sources/Slipstream/W3C/Elements/Sections/H5.swift b/Sources/Slipstream/W3C/Elements/Sections/H5.swift new file mode 100644 index 0000000..37d493b --- /dev/null +++ b/Sources/Slipstream/W3C/Elements/Sections/H5.swift @@ -0,0 +1,33 @@ +/// A view that represents a heading for a section. +/// +/// ```swift +/// struct MySiteContent: View { +/// var body: some View { +/// Body { +/// H5("Hello, world!") +/// } +/// } +/// } +/// ``` +/// +/// - SeeAlso: W3C [`h1-h6`](https://html.spec.whatwg.org/multipage/sections.html#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements) specification. +@available(iOS 17.0, macOS 14.0, *) +public struct H5: W3CElement where Content: View { + @_documentation(visibility: private) + public let tagName: String = "h5" + + @_documentation(visibility: private) + @ViewBuilder public let content: () -> Content + + /// Creates an H5 view. + public init(@ViewBuilder content: @escaping () -> Content) { + self.content = content + } + + /// Creates an H5 view with some static text. + public init(_ text: String) where Content == Text { + self.content = { + Text(text) + } + } +} diff --git a/Sources/Slipstream/W3C/Elements/Sections/H6.swift b/Sources/Slipstream/W3C/Elements/Sections/H6.swift new file mode 100644 index 0000000..9e317ae --- /dev/null +++ b/Sources/Slipstream/W3C/Elements/Sections/H6.swift @@ -0,0 +1,33 @@ +/// A view that represents a heading for a section. +/// +/// ```swift +/// struct MySiteContent: View { +/// var body: some View { +/// Body { +/// H6("Hello, world!") +/// } +/// } +/// } +/// ``` +/// +/// - SeeAlso: W3C [`h1-h6`](https://html.spec.whatwg.org/multipage/sections.html#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements) specification. +@available(iOS 17.0, macOS 14.0, *) +public struct H6: W3CElement where Content: View { + @_documentation(visibility: private) + public let tagName: String = "h6" + + @_documentation(visibility: private) + @ViewBuilder public let content: () -> Content + + /// Creates an H6 view. + public init(@ViewBuilder content: @escaping () -> Content) { + self.content = content + } + + /// Creates an H6 view with some static text. + public init(_ text: String) where Content == Text { + self.content = { + Text(text) + } + } +} diff --git a/Tests/SlipstreamTests/Sites/CatalogSiteTests.swift b/Tests/SlipstreamTests/Sites/CatalogSiteTests.swift index 4ea9382..d14c73b 100644 --- a/Tests/SlipstreamTests/Sites/CatalogSiteTests.swift +++ b/Tests/SlipstreamTests/Sites/CatalogSiteTests.swift @@ -17,6 +17,14 @@ private struct CatalogSite: View { Body { Container { Text("Hello, world!") + H1("Heading 1") + H2 { + Text("Heading 2") + } + H3("Heading 3") + H4("Heading 4") + H5("Heading 5") + H6("Heading 6") } } .id("root") @@ -38,6 +46,12 @@ struct CatalogSiteTests {
Hello, world! +

Heading 1

+

Heading 2

+

Heading 3

+

Heading 4

+
Heading 5
+
Heading 6
diff --git a/Tests/SlipstreamTests/Views/W3C/HeadingTests.swift b/Tests/SlipstreamTests/Views/W3C/HeadingTests.swift new file mode 100644 index 0000000..43f0757 --- /dev/null +++ b/Tests/SlipstreamTests/Views/W3C/HeadingTests.swift @@ -0,0 +1,32 @@ +import Testing + +import Slipstream + +struct HeadingTests { + @Test func emptyBlock() throws { + try #expect(renderHTML(H1 {}) == "

") + try #expect(renderHTML(H2 {}) == "

") + try #expect(renderHTML(H3 {}) == "

") + try #expect(renderHTML(H4 {}) == "

") + try #expect(renderHTML(H5 {}) == "
") + try #expect(renderHTML(H6 {}) == "
") + } + + @Test func withText() throws { + try #expect(renderHTML(H1 { Text("Hello, world!") }) == "

Hello, world!

") + try #expect(renderHTML(H2 { Text("Hello, world!") }) == "

Hello, world!

") + try #expect(renderHTML(H3 { Text("Hello, world!") }) == "

Hello, world!

") + try #expect(renderHTML(H4 { Text("Hello, world!") }) == "

Hello, world!

") + try #expect(renderHTML(H5 { Text("Hello, world!") }) == "
Hello, world!
") + try #expect(renderHTML(H6 { Text("Hello, world!") }) == "
Hello, world!
") + } + + @Test func attribute() throws { + try #expect(renderHTML(H1 {}.language("en")) == #"

"#) + try #expect(renderHTML(H2 {}.language("en")) == #"

"#) + try #expect(renderHTML(H3 {}.language("en")) == #"

"#) + try #expect(renderHTML(H4 {}.language("en")) == #"

"#) + try #expect(renderHTML(H5 {}.language("en")) == #"
"#) + try #expect(renderHTML(H6 {}.language("en")) == #"
"#) + } +}